diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e482eb7..7d9b211 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 7ad6b9d..d43b3bc 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 6175663..0000000 --- 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 68477e0..b329dec 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 0000000..83a1bdd --- /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 0000000..26bebaf --- /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 0000000..34ccf26 --- /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 5fa7b21..530972f 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 ef44e7d..4a70931 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 1315b65..c089618 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 5eb12a3..233ac5c 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 c5c7051..0000000 --- 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 2e02a03..661d90a 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 08641fd..971ec5d 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 88740ec..6ce4d3f 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 064dc20..342d3bc 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 8ba3501..d667120 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 R = Type.Ref('T') // const R = { - // $ref: 'T' +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' // } -type R = Static // type R = string +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 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,204 +866,155 @@ function test(node: Node) { } ``` - - -### Conditional 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. - -```typescript -// TypeScript - -type T0 = string extends number ? true : false // type T0 = false - -type T1 = Extract<(1 | 2 | 3), 1> // type T1 = 1 - -type T2 = Exclude<(1 | 2 | 3), 1> // type T2 = 2 | 3 - -// 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 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 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 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 T = `option${'A'|'B'|'C'}` // type T = 'optionA' | 'optionB' | 'optionC' +type K = `prop${'A'|'B'|'C'}` // type T = 'propA' | 'propB' | 'propC' -type R = Record // type R = { - // optionA: string - // optionB: string - // optionC: string +type R = Record // type R = { + // propA: string + // propB: string + // propC: string // } // TypeBox -const T = Type.TemplateLiteral('option${A|B|C}') // const T = { - // pattern: '^option(A|B|C)$', - // type: 'string' - // } +const K = Type.TemplateLiteral('prop${A|B|C}') // const K: TTemplateLiteral<[ + // TLiteral<'prop'>, + // TUnion<[ + // TLiteral<'A'>, + // TLiteral<'B'>, + // TLiteral<'C'>, + // ]> + // ]> -const R = Type.Record(T, Type.String()) // const R = { - // type: 'object', - // required: ['optionA', 'optionB'], - // properties: { - // optionA: { - // type: 'string' - // }, - // optionB: { - // type: 'string' - // } - // optionC: { - // type: 'string' - // } - // } - // } +const R = Type.Record(K, Type.String()) // const R: TObject<{ + // hello1: TString, + // hello2: TString, + // hello3: TString, + // }> ``` ### Indexed Access 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 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 -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 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']) // const A = { type: 'number' } +const A = Type.Index(T, ['x']) // type A = T['x'] + // + // ... evaluated as + // + // const A: TNumber -const B = Type.Index(T, ['x', 'y']) // const B = { - // anyOf: [ - // { type: 'number' }, - // { type: 'string' } - // ] - // } +const B = Type.Index(T, ['x', 'y']) // type B = T['x' | 'y'] + // + // ... evaluated as + // + // const B: TUnion<[ + // TNumber, + // TString, + // ]> -const C = Type.Index(T, Type.KeyOf(T)) // const C = { - // anyOf: [ - // { type: 'number' }, - // { type: 'string' }, - // { type: 'boolean' } - // ] - // } +const C = Type.Index(T, Type.KeyOf(T)) // type C = T[keyof T] + // + // ... evaluated as + // + // const C: TUnion<[ + // TNumber, + // TString, + // TBoolean + // ]> ``` - + -### Rest Types +### Mapped 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 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. ```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 T = Type.Object({ // type T = { + x: Type.Number(), // x: number, + y: Type.String(), // y: string, + z: Type.Boolean() // z: boolean +}) // } -const R = Type.Rest(T) // const R = [ - // { type: 'string' }, - // { type: 'number' } - // ] - -const U = Type.Union(R) // const U = { - // anyOf: [ - // { type: 'string' }, - // { type: 'number' } - // ] - // } +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 + // }> ``` - + -### Transform Types +### Conditional 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 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 -import { Value } from '@sinclair/typebox/value' +// 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 T = Type.Transform(Type.Number()) - .Decode(value => new Date(value)) // required: number to Date - .Encode(value => value.getTime()) // required: Date to number +// 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 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' - -const T = Type.Transform(Type.Array(Type.Number(), { uniqueItems: true })) - .Decode(value => new Set(value)) - .Encode(value => [...value]) - -type D = StaticDecode // type D = Set -type E = StaticEncode // type E = Array -type T = Static // type T = Array +// 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>, +) // ]> ``` ### Intrinsic 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 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<[ @@ -1004,6 +1032,55 @@ const C = Type.Capitalize( // const C: TTemplateLitera // ]> ``` + + +### 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 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)) // decode: number to Date + .Encode(value => value.getTime()) // encode: Date to number + +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 +import { Static, StaticDecode, StaticEncode } from '@sinclair/typebox' + +const T = Type.Transform(Type.Array(Type.Number(), { uniqueItems: true })) + .Decode(value => new Set(value)) + .Encode(value => [...value]) + +type D = StaticDecode // type D = Set +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 @@ -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,94 +1576,13 @@ 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. - - - -### Types - -Use the TypeSystem Type function to register a user defined type. - -```typescript -import { TypeSystem } from '@sinclair/typebox/system' - -const StringSet = TypeSystem.Type>('StringSet', (options, value) => { - return value instanceof Set && [...value].every(value => typeof value === 'string') -}) - -const T = StringSet({}) // Pass options if any - -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 - -``` - - - -### Formats - -Use the TypeSystem Format function to register a string format. - -```typescript -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 -``` - - - -### Errors - -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' - -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: '예상 부울' - // }] -``` +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. ### 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. +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. The following overrides are available. @@ -1573,11 +1609,58 @@ 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 +// Used to signal void return on Json-Rpc 2.0 protocol TypeSystemPolicy.AllowNullVoid = true ``` + + +## 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. + +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. + + +```typescript +import { SetErrorFunction, DefaultErrorFunction, ValueErrorType } 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) + } +}) +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: { ... }, + 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: '예상 부울' + // }] +``` + ## 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 fe03f6d..7f7368c 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 cc91cb4..48b4c10 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 9f09e1f..c84ebab 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 0000000..bba4ff5 --- /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 71cdce9..b5a10ef 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 0000000..6c24390 --- /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 21c978a..fe79f6d 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 0000000..04c4a4e --- /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 5282862..0c1535b 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 c78307c..e53e3eb 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 0000000..e462db9 --- /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 0000000..29688e1 --- /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 0000000..1165e62 --- /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 0000000..ae02b63 --- /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 0000000..9ee5f87 --- /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 0000000..d6bc7cb --- /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 0000000..8eec616 --- /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 0000000..26a23a5 --- /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 0000000..53b0fb2 --- /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 0000000..ce8f4fb --- /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 0000000..7d3fe1a --- /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 0000000..aad153c --- /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 0000000..44bd8cb --- /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 0000000..08d1c3f --- /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 0000000..03e8804 --- /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 0000000..0bfe804 --- /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 0000000..2fe294b --- /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 0000000..b3ce2ac --- /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 0000000..f13d0f5 --- /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 0000000..e2cb70e --- /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 0000000..23f9c26 --- /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 0000000..c4e37e8 --- /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 0000000..8e75965 --- /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 0000000..9e45ec2 --- /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 0000000..932ee5d --- /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 0000000..84fa784 --- /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 0000000..496b4d6 --- /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 0000000..f531f4d --- /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 0000000..43fb9af --- /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 0000000..4a92aee --- /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 0000000..d5dfd4a --- /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 0000000..6523526 --- /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 0000000..277d5e0 --- /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 0000000..858bbfd --- /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 0000000..751e9e1 --- /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 0000000..b83e080 --- /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 0000000..d726d91 --- /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 0000000..acc955e --- /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 0000000..d34c5dc --- /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 0000000..f6c275c --- /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 0000000..e91a9d6 --- /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 0000000..330381d --- /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 0000000..9619192 --- /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 0000000..2ef4998 --- /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 0000000..3fd3107 --- /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 0000000..7c97429 --- /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 0000000..3399c72 --- /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 0000000..2e7cc05 --- /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 0000000..9794a4f --- /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 0000000..e09fb9b --- /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 0000000..84263c6 --- /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 0000000..4d6559c --- /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 0000000..d47dbf3 --- /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 0000000..bdd973a --- /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 0000000..5b1567c --- /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 0000000..f685717 --- /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 0000000..58f6898 --- /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 0000000..bea542a --- /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 0000000..aa85b71 --- /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 0000000..f265034 --- /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 0000000..73513eb --- /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 0000000..23ed802 --- /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 0000000..c2f3d3b --- /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 0000000..815c27e --- /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 0000000..5f6f25b --- /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 75399a0..991c367 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 0000000..b06c1dd --- /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 0000000..0bb053d --- /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 0000000..d5c9aed --- /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 0000000..b1c1b5f --- /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 0000000..5080aa6 --- /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 0000000..835d39b --- /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 0000000..d695022 --- /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 0000000..52ee4fa --- /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 0000000..8c41f54 --- /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 0000000..74a37a2 --- /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 0000000..03a32c0 --- /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 0000000..9770929 --- /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 0000000..63cb1a4 --- /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 0000000..1c61921 --- /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 0000000..d4cb53e --- /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 0000000..8754ffd --- /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 0000000..5e1f19b --- /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 0000000..24ad795 --- /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 0000000..84908ea --- /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 0000000..49ab254 --- /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 0000000..9135044 --- /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 0000000..7fc22f7 --- /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 0000000..49cf9dc --- /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 0000000..df0a93a --- /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 0000000..281bef4 --- /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 0000000..6dad0c7 --- /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 0000000..c43f623 --- /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 0000000..1ae3ed8 --- /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 0000000..5bebd58 --- /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 0000000..a68c53f --- /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 0000000..c5e4441 --- /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 0000000..efac50d --- /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 0000000..c6dd8b9 --- /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 0000000..e0afd10 --- /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 0000000..b861f8e --- /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 0000000..28ad4ea --- /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 0000000..867117b --- /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 0000000..195fafb --- /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 0000000..e2d4bb2 --- /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 0000000..af7c8ba --- /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 0000000..23fb89d --- /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 0000000..d49dad8 --- /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 0000000..7dbf0a2 --- /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 0000000..d9b9e8d --- /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 0000000..1fa9844 --- /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 0000000..b5e30fa --- /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 0000000..4da4a61 --- /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 0000000..336d475 --- /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 0000000..5515095 --- /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 0000000..48b99d0 --- /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 0000000..3e44a84 --- /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 0000000..788ede1 --- /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 0000000..f39c084 --- /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 0000000..9f303bb --- /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 0000000..c5bc936 --- /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 0000000..10eb1ce --- /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 0000000..ec724bd --- /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 0000000..9bb19d9 --- /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 0000000..cff19af --- /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 0000000..1db9fa4 --- /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 0000000..1df55fe --- /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 0000000..00fab30 --- /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 0000000..3029d42 --- /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 0000000..ebcf3d7 --- /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 0000000..a0fb0e0 --- /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 0000000..78e313b --- /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 0000000..428fd82 --- /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 0000000..2f6da28 --- /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 0000000..e26e8f8 --- /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 0000000..b1442f4 --- /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 0000000..8ca4507 --- /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 0000000..610d3ad --- /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 0000000..fd31f8d --- /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 0000000..44fa18e --- /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 0000000..bfd714b --- /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 0000000..287088f --- /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 0000000..e672ba0 --- /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 0000000..c478204 --- /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 0000000..8f4f0c9 --- /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 0000000..e38b376 --- /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 0000000..caf80ea --- /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 0000000..de47e42 --- /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 0000000..1c188b4 --- /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 0000000..5bcda71 --- /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 0000000..abd3062 --- /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 0000000..4c5adc9 --- /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 0000000..bac16a3 --- /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 0000000..dbb90a9 --- /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 0000000..43a03ac --- /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 0000000..671dd40 --- /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 0000000..17ae275 --- /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 0000000..f856a1e --- /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 0000000..6af7e3f --- /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 0000000..f70df69 --- /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 0000000..c3436b8 --- /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 0000000..9de4dbc --- /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 0000000..4e6ab02 --- /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 0000000..cb70995 --- /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 0000000..c1eb788 --- /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 0000000..32119f7 --- /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 0000000..081b459 --- /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 0000000..7975887 --- /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 0000000..d4e6214 --- /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 0000000..53f2754 --- /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 0000000..4c2296a --- /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 0000000..1c1732c --- /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 0000000..cbc1c28 --- /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 0000000..a069fe0 --- /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 0000000..17dd10d --- /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 0000000..c2f2619 --- /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 0000000..248d4e6 --- /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 0000000..6eb5ba4 --- /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 0000000..5c5265e --- /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 0000000..7d9c7e2 --- /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 0000000..f5a0d9a --- /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 0000000..78141b3 --- /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 0000000..26fb987 --- /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 0000000..3b9bf51 --- /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 0000000..867f33f --- /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 57e8cb2..0000000 --- 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 16e3ec9..8ac9e79 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 ValueCastError extends TypeBoxError { + constructor(public readonly schema: TSchema, message: string) { + super(message) } } -export class ValueCastNeverTypeError extends Types.TypeBoxError { - constructor(public readonly schema: Types.TSchema) { - super('Never types cannot be cast') +// ------------------------------------------------------------------ +// 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 } } -export class ValueCastRecursiveTypeError extends Types.TypeBoxError { - constructor(public readonly schema: Types.TSchema) { - super('Cannot cast recursive schemas') - } -} -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. -// -------------------------------------------------------------------------- -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 0000000..5d403f7 --- /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 7e98770..c2cd892 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 0000000..e318a65 --- /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 0000000..29c52d3 --- /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 0000000..4e55b27 --- /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 a2e2d1f..4c33792 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 0000000..4294f99 --- /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 60ba2e1..8a0de37 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 +// 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 TRecord(schema: Types.TRecord, references: Types.TSchema[], value: any): unknown { +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 +// 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 TUndefined(schema: Types.TUndefined, references: Types.TSchema[], value: any): unknown { +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 0000000..0e642e6 --- /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 b36b42f..b632947 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 ValueCreateError extends TypeBoxError { + constructor(public readonly schema: TSchema, message: string) { + super(message) } } -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.') - } -} -// -------------------------------------------------------------------------- -// 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 TNot(schema: Types.TNot, references: Types.TSchema[]): any { +function FromNever(schema: TNever, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { return schema.default } else { - throw new ValueCreateNotTypeError(schema) + throw new ValueCreateError(schema, 'Never types cannot be created. Consider using a default value.') } } -function TNull(schema: Types.TNull, references: Types.TSchema[]): any { +function FromNot(schema: TNot, references: TSchema[]): any { + if (HasPropertyKey(schema, 'default')) { + return 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 } 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 0000000..62543e7 --- /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 0000000..6a310d2 --- /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 0000000..fb74557 --- /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 5a2092d..d7ae9fa 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 0000000..3eca378 --- /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 cf9ebd7..e7c0926 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 0000000..d0bf7e4 --- /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 558d96f..2640939 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 0000000..cf0e42f --- /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 5d5bb17..92aa187 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 0000000..36d7e3f --- /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 cd8935e..6964af0 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 0000000..a260784 --- /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 e8beb3b..eecc447 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 0000000..cf8bd92 --- /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 42d563c..670a02e 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 995ce81..0000000 --- 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 0000000..d407196 --- /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 0000000..e0c4639 --- /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 bb377f9..0000000 --- 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 0000000..8908c9d --- /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 0000000..e91057e --- /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 0000000..437fad1 --- /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 0000000..f821f6d --- /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 9cf2716..0000000 --- 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 0000000..3af158d --- /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 0000000..6be3177 --- /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 5210313..546145c 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 0000000..5976be4 --- /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 8325c80..fe2ff6a 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 a6e2e43..99d3ced 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 98b61c0..ee0ade8 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 0000000..15c9688 --- /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 0000000..5a423c1 --- /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 0000000..05a0a10 --- /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 0000000..1db93a6 --- /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 0000000..fd7cf77 --- /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 0000000..9d34cc8 --- /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 0000000..5b36ce9 --- /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 0000000..66549ed --- /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 0000000..a3305a1 --- /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 0000000..f033f22 --- /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 2e9a929..315eec2 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 0000000..21705e2 --- /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 0c6e11f..ae974b9 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 5e56d70..5be5691 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 7df4b18..73820cf 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 0000000..71419b1 --- /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 1264822..baf279d 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 0000000..a852675 --- /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 db87a1e..487c94a 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 870e697..74b71aa 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 8d5d9ea..5a9bf09 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 14ca1bf..fb071fe 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 dcfae6e..6ee16cb 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 44730ba..3329f6b 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 0000000..e6f1c69 --- /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 3dd3e83..6373c9a 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 b734bc8..2e468c8 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 4eba4c0..9cc5392 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 5346cfa..f965eab 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 1d56e21..6e336f3 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 f5b6151..a590c84 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 04df6c6..2bcfa88 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 0b33408..e889555 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 c826cc0..0331132 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 1c5d009..6abd575 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 1cf5b6b..d685dfa 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 7c23890..ca2c952 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 53f8d1e..fd3a97a 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 702e162..157d7d2 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 aa647ec..ce9c418 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 8bc3a6e..76e4058 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 843786a..2313c87 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 7ac3301..905a96f 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 0000000..2298acd --- /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 0dd95ec..cdb3e21 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 03dc7aa..9412f4f 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 3dd6ff3..1e8aeab 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 b297655..99bc360 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 828163d..0128fb0 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 6641261..4996a10 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 e9eb05c..39783cf 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 85dbf86..7622dbf 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 106e175..48efc64 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 bb35ccc..ecc4cc9 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 95b0063..12f0a9e 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 8b3567e..55a52da 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 ef535a9..7515107 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 bc16722..ae7edab 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 ecc50b9..e74af2e 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 33ed2e9..def31e7 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 7d4fc1f..e8a3f21 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 0000000..0e5ad42 --- /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 967d04c..bcbb78a 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 a9534a4..fdfc720 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 0000000..eedd3a6 --- /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 fd4ee99..dc092e8 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 fe4f06d..78fbdfe 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 9fe5f35..b3524d1 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 79e66ba..400c830 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 3533d6d..271b53f 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 2a680f8..0e29896 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 849aabd..42a8c61 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 25c069c..ab2b584 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 b0dd08c..df549ed 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 d8b24ad..1b066c4 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 52c87f8..2600695 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 b982a5e..c1e6b21 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 5a07e5d..be9e797 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 0000000..1ab3daf --- /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 71e5695..b21cb5d 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 dbeca23..0ab3792 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 02b9116..02ba9d6 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 4f7cfcd..ff5b2f0 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 7b19d14..c449cc7 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 cd248bb..6c419f4 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 0fcd905..7b53d4d 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 f00ecc7..7fab077 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 987d8bd..89ec3df 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 8585e17..57c6fe7 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 83682d2..e2b5a74 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 0000000..845c175 --- /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 5af8134..e4315e9 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 4c22803..dc018d6 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 50205a1..5eb8626 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 28041bf..8723b21 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 7637133..b24518e 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 93508d5..8ca3a7e 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 9326c23..31a15c2 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 cc058d8..de668fd 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 d54b4e5..ff41267 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 8393bff..a6389f4 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 62b485e..edcf340 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 bd5cfcd..cc488fd 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 f43a2d7..5691abe 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 602ddf4..539a937 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 fb60e97..9c1f52b 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 bc394a5..f5cfcd7 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 3ff191a..a6a18f3 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 908f161..53fad9c 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 e1dc981..196ee0f 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 c149c66..543f9b5 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 34837db..cf73898 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 9f41942..ef463f5 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 3c5d468..2ab700f 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 0000000..a6a97ef --- /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 0000000..2178571 --- /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 032be5d..483e679 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 5ab50af..57db98f 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 6fdf85c..1a8b13f 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 53028df..0000000 --- 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 73aa9cf..0000000 --- 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 5165235..d68d189 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 6b4e059..6c036fa 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 0000000..4b3db9f --- /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 7b51cf3..9012725 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 0000000..9a49a25 --- /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 0000000..6e8a218 --- /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 0000000..a067233 --- /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 0000000..cdd77d4 --- /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 0000000..7697d25 --- /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 0000000..035f335 --- /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 0000000..4a4be49 --- /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 0000000..8f777e4 --- /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 0000000..8890204 --- /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 0000000..2bc5a4e --- /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 0000000..b290dd2 --- /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 0000000..ff37704 --- /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 0000000..14f5d52 --- /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 0000000..ec19feb --- /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 0000000..9119129 --- /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 0000000..8a4b836 --- /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 0000000..bf424b7 --- /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 0000000..2945598 --- /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 0000000..cb60a05 --- /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 0000000..a52d81a --- /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 0000000..aafd930 --- /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 0000000..805665a --- /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 0000000..0d2d7e4 --- /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 0000000..6ef341c --- /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 0000000..1a6bb6e --- /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 0000000..b065561 --- /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 0000000..6b1d574 --- /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 0000000..aa5d757 --- /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 0000000..62047d2 --- /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 0000000..f767305 --- /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 0000000..81618f0 --- /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 0000000..732eb5b --- /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 0000000..4b590c4 --- /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 0000000..443e3d6 --- /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 0000000..edba3eb --- /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 0000000..cd4c20d --- /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 f1415fd..cc42439 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 0000000..15b12e9 --- /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 0000000..be385e6 --- /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 0000000..dd26f57 --- /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 0000000..b7e8aa7 --- /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 0000000..ee05bb0 --- /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 0000000..8a73e41 --- /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 0000000..76cb234 --- /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 0000000..2522cf2 --- /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 0000000..ae6f9ec --- /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 0000000..959c282 --- /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 0000000..7395f9f --- /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 0000000..c0ce64b --- /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 0000000..0e72b3e --- /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 0000000..d49b7f2 --- /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 0000000..2cb0bb2 --- /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 0000000..1f8a96f --- /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 0000000..fa01d55 --- /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 0000000..a395427 --- /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 0000000..148756d --- /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 0000000..9246fe8 --- /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 0000000..6dcaa70 --- /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 0000000..ab4dda4 --- /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 0000000..659e052 --- /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 0000000..4b300dd --- /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 0000000..417727b --- /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 0000000..6340595 --- /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 0000000..9c32c7a --- /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 0000000..af83333 --- /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 0000000..d593fb7 --- /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 0000000..2dd6b7e --- /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 0000000..c7acdc9 --- /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 0000000..5c3cfa7 --- /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 0000000..d622cd1 --- /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 0000000..56b572c --- /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 0000000..9723657 --- /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 0000000..03d03c1 --- /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 d515111..ee8d383 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 8d06d50..aceb064 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 de126d7..4e6ce99 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 06d8241..22a2b1d 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 93eeff8..4315241 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 9a10c8c..862731d 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 0000000..d81d0fe --- /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 0000000..4df0737 --- /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 911f5ec..df90d85 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 a33af51..fe5875b 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 0000000..9f29b57 --- /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 b334269..619930d 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 4d16683..1d6224f 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 e213565..e284ce0 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 f0ec844..670b0b6 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 917628a..1f41762 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 9c9895e..3decc9e 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 ec148af..6a47a31 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