diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e482eb7e1..7d9b21121 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,7 +8,7 @@ jobs: node: [16.x, 18.x, 20.x] os: [ubuntu-latest, windows-latest, macOS-latest] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install Node uses: actions/setup-node@v3 with: diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 7ad6b9d88..d43b3bc2c 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -10,7 +10,7 @@ jobs: node: [20.x] os: [ubuntu-latest] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - name: Install Node uses: actions/setup-node@v3 with: diff --git a/benchmark/measurement/index.ts b/benchmark/measurement/index.ts deleted file mode 100644 index 6175663af..000000000 --- a/benchmark/measurement/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { shell } from '@sinclair/hammer' - -export async function measurement() { - await shell(`hammer run benchmark/measurement/module/index.ts --dist target/benchmark/measurement`) -} diff --git a/changelog/0.32.0.md b/changelog/0.32.0.md new file mode 100644 index 000000000..7038996f5 --- /dev/null +++ b/changelog/0.32.0.md @@ -0,0 +1,97 @@ +## [0.32.0](https://www.npmjs.com/package/@sinclair/typebox/v/0.32.0) + +## Overview + +Revision 0.32.0 is milestone revision. This revision carries out a complete modularization of `Type.*` and `Value.*`, as well as remodelling the TypeBox API to support selectively importing individual types. In addition to project restructuring, this revision also includes two new Types (`Const` and `Deref`) two new Value functions (`Clean` and `Default`) as well as many additional optimizations to type inference. + +This is a significant revision for TypeBox. The updates in this revision should not break most users, however, many internal inference types that were previously exported on `typebox.ts` have been made private within module. Testing has been carried out to ensure the documented frontend for TypeBox is retained, however due to internal inference types being made fully private. Some users may experience some breakage. + +A minor revision is required. + +## Contents + +- Enhancements + - [Modularization](#Modularization) + - [Types](#Modularization-Types) + - [Values](#Modularization-Values) + - [Bundle Size](#Modularization-Bundle-Size) + - [Types](#Types) + - [Const](#Const) + - [Deref](#Deref) + - [Values](#Values) + - [Clean](#Clean) + - [Default](#Default) +- Deprecations + - [RegExp](#RegExp) + - [TypeSystem Format](#TypeSystemFormat) + - [TypeSystem Type](#TypeSystemType) +- Breaking + - [JsonTypeBuilder and JavaScriptTypeBuilder](#JsonTypeBuilder-and-JavaScriptTypeBuilder) + - [TypeSystemPolicy](#TypeSystemPolicy) + +## Modularization + +Revision 0.32.0 carries out a complete restructuring of TypeBox's internals to modularize the type system. These updates enable for the following. + +- Enable bundlers to properly tree-shake TypeBox's type system. +- Enable additional options for integrators re-exporting TypeBox's interface. +- Further optimize bundle sizes for Value and TypeCompiler. +- Improve maintainability of the type system. +- Make nessasarily provisions for ESM publishing. + +### Types + +Revision 0.32.0 now exposes `Type.*` as a default export as well as enables all types to be selectively imported. When importing selective types, bundlers may be able to tree-shake unused types. The following imports are now supported. + +```typescript +import { Type, type Static } from '@sinclair/typebox' // classic + +import Type, { type Static } from '@sinclair/typebox' // modern + +import { Object, String, Number, ... } from '@sinclair/typebox' // selective +``` + +### Values + +Revision 0.32.0 carries out modularization of `Value.*`. Individual Value functions can not be imported directly without specifying the sub module path. + +```typescript +import { Value } from '@sinclair/typebox/value' // classic + +import Value from '@sinclair/typebox/value' // modern + +import { Check, Clone, Cast } from '@sinclair/typebox/value' // selective +``` + +### Bundle Size + +The modularization in 0.32.0 has enabled significant bundle size optimizations across Value, TypeCompiler, Errors and System sub modules. + +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 │ '109.3 kb' │ ' 48.5 kb' │ '2.25 x' │ +│ typebox/errors │ ' 54.8 kb' │ ' 24.9 kb' │ '2.20 x' │ +│ typebox/system │ ' 12.1 kb' │ ' 6.1 kb' │ '1.98 x' │ +│ typebox/type │ ' 77.8 kb' │ ' 32.8 kb' │ '2.37 x' │ +│ typebox/value │ '143.7 kb' │ ' 61.2 kb' │ '2.35 x' │ +│ typebox │ ' 77.9 kb' │ ' 32.8 kb' │ '2.37 x' │ +└──────────────────────┴────────────┴────────────┴─────────────┘ +``` \ No newline at end of file diff --git a/examples/annotation/annotation.ts b/examples/annotation/annotation.ts new file mode 100644 index 000000000..41b0533cd --- /dev/null +++ b/examples/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.TOptional(TK) && Types.TypeGuard.TReadonly(TK) ? `readonly ${K}?: ${Visit(TK, references)}` : + Types.TypeGuard.TReadonly(TK) ? `readonly ${K}: ${Visit(TK, references)}` : + Types.TypeGuard.TOptional(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.IsTemplateLiteralFinite(E)) return 'string' + return [...Types.TemplateLiteralGenerate(E)].map((literal) => `'${Escape(literal)}'`).join(' | ') + } + function Visit(schema: Types.TSchema, references: Types.TSchema[]): string { + // prettier-ignore + return ( + Types.TypeGuard.TAny(schema) ? 'any' : + Types.TypeGuard.TArray(schema) ? `${Visit(schema.items, references)}[]` : + Types.TypeGuard.TAsyncIterator(schema) ? `AsyncIterableIterator<${Visit(schema.items, references)}>` : + Types.TypeGuard.TBigInt(schema) ? `bigint` : + Types.TypeGuard.TBoolean(schema) ? `boolean` : + Types.TypeGuard.TConstructor(schema) ? `new (${Parameters(schema.parameter, 0, references)}) => ${Visit(schema.returns, references)}` : + Types.TypeGuard.TDate(schema) ? 'Date' : + Types.TypeGuard.TFunction(schema) ? `(${Parameters(schema.parameters, 0, references)}) => ${Visit(schema.returns, references)}` : + Types.TypeGuard.TInteger(schema) ? 'number' : + Types.TypeGuard.TIntersect(schema) ? `(${Intersect(schema.allOf, references)})` : + Types.TypeGuard.TIterator(schema) ? `IterableIterator<${Visit(schema.items, references)}>` : + Types.TypeGuard.TLiteral(schema) ? `${Literal(schema, references)}` : + Types.TypeGuard.TNever(schema) ? `never` : + Types.TypeGuard.TNull(schema) ? `null` : + Types.TypeGuard.TNot(schema) ? 'unknown' : + Types.TypeGuard.TNumber(schema) ? 'number' : + Types.TypeGuard.TObject(schema) ? `{ ${Properties(schema.properties, Object.getOwnPropertyNames(schema.properties), references)} }` : + Types.TypeGuard.TPromise(schema) ? `Promise<${Visit(schema.item, references)}>` : + Types.TypeGuard.TRecord(schema) ? `${Record(schema, references)}` : + Types.TypeGuard.TRef(schema) ? `${Visit(Types.Type.Deref(schema, references), references)}` : + Types.TypeGuard.TString(schema) ? 'string' : + Types.TypeGuard.TSymbol(schema) ? 'symbol' : + Types.TypeGuard.TTemplateLiteral(schema) ? `${TemplateLiteral(schema, references)}` : + Types.TypeGuard.TThis(schema) ? 'unknown' : // requires named interface + Types.TypeGuard.TTuple(schema) ? `[${Tuple(schema.items || [], references)}]` : + Types.TypeGuard.TUint8Array(schema) ? `Uint8Array` : + Types.TypeGuard.TUndefined(schema) ? 'undefined' : + Types.TypeGuard.TUnion(schema) ? `${Union(schema.anyOf, references)}` : + Types.TypeGuard.TVoid(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/examples/annotation/index.ts b/examples/annotation/index.ts new file mode 100644 index 000000000..34ccf2617 --- /dev/null +++ b/examples/annotation/index.ts @@ -0,0 +1 @@ +export * from './annotation' \ No newline at end of file diff --git a/examples/collections/array.ts b/examples/collections/array.ts index 90f5d73b4..1a7a7550b 100644 --- a/examples/collections/array.ts +++ b/examples/collections/array.ts @@ -27,18 +27,18 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ import { TypeCheck, TypeCompiler, ValueError } from '@sinclair/typebox/compiler' -import { TSchema, Static, TypeBoxError } from '@sinclair/typebox' +import { TSchema, Static } from '@sinclair/typebox' import { Value } from '@sinclair/typebox/value' // ---------------------------------------------------------------- // TypeArrayError // ---------------------------------------------------------------- -export class TypeArrayError extends TypeBoxError { +export class TypeArrayError extends Error { constructor(message: string) { super(`${message}`) } } -export class TypeArrayLengthError extends TypeBoxError { +export class TypeArrayLengthError extends Error { constructor() { super('arrayLength not a number') } diff --git a/examples/collections/map.ts b/examples/collections/map.ts index 796140fe6..2fa3dffed 100644 --- a/examples/collections/map.ts +++ b/examples/collections/map.ts @@ -27,18 +27,18 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ import { TypeCheck, TypeCompiler, ValueError } from '@sinclair/typebox/compiler' -import { TSchema, Static, TypeBoxError } from '@sinclair/typebox' +import { TSchema, Static } from '@sinclair/typebox' import { Value } from '@sinclair/typebox/value' // ---------------------------------------------------------------- // TypeMapKeyError // ---------------------------------------------------------------- -export class TypeMapKeyError extends TypeBoxError { +export class TypeMapKeyError extends Error { constructor(message: string) { super(`${message} for key`) } } -export class TypeMapValueError extends TypeBoxError { +export class TypeMapValueError extends Error { constructor(key: unknown, message: string) { super(`${message} for key ${JSON.stringify(key)}`) } diff --git a/examples/collections/set.ts b/examples/collections/set.ts index 8cf312a3e..4e8d31213 100644 --- a/examples/collections/set.ts +++ b/examples/collections/set.ts @@ -27,13 +27,13 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ import { TypeCheck, TypeCompiler, ValueError } from '@sinclair/typebox/compiler' -import { TSchema, Static, TypeBoxError } from '@sinclair/typebox' +import { TSchema, Static } from '@sinclair/typebox' import { Value } from '@sinclair/typebox/value' // ---------------------------------------------------------------- // Errors // ---------------------------------------------------------------- -export class TypeSetError extends TypeBoxError { +export class TypeSetError extends Error { constructor(message: string) { super(`${message}`) } diff --git a/examples/prototypes/evaluate.ts b/examples/prototypes/evaluate.ts index 5fa7b2128..5260def0f 100644 --- a/examples/prototypes/evaluate.ts +++ b/examples/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> : diff --git a/examples/prototypes/index.ts b/examples/prototypes/index.ts index ef44e7d95..4a70931c8 100644 --- a/examples/prototypes/index.ts +++ b/examples/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/examples/prototypes/partial-deep.ts index 1315b65b0..aa27a07aa 100644 --- a/examples/prototypes/partial-deep.ts +++ b/examples/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> : diff --git a/examples/typedef/typedef.ts b/examples/typedef/typedef.ts index 5eb12a372..a5a59d497 100644 --- a/examples/typedef/typedef.ts +++ b/examples/typedef/typedef.ts @@ -27,21 +27,10 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ import { TypeSystemErrorFunction, DefaultErrorFunction } from '@sinclair/typebox/system' -import * as Types from '@sinclair/typebox' +import * as Types from '@sinclair/typebox/type/index.mjs' // -------------------------------------------------------------------------- -// 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,8 +54,8 @@ 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>> +export type InferUnion = T extends [infer L, ...infer R] + ? Types.Evaluate<{ [_ in D]: Index } & Types.Static>> | InferUnion, D, Types.Increment>> : never export interface TUnion extends Types.TSchema { @@ -177,7 +166,7 @@ export interface StructMetadata extends Metadata { } export interface TStruct extends Types.TSchema, StructMetadata { [Types.Kind]: 'TypeDef:Struct' - static: Types.PropertiesReduce + static: Types.ObjectResolve optionalProperties: { [K in Types.Assert, keyof T>]: T[K] } properties: { [K in Types.Assert, keyof T>]: T[K] } } @@ -240,7 +229,7 @@ export namespace TimestampFormat { // -------------------------------------------------------------------------- // ValueCheck // -------------------------------------------------------------------------- -export class ValueCheckError extends Types.TypeBoxError { +export class ValueCheckError extends Error { constructor(public readonly schema: Types.TSchema) { super('Unknown type') } diff --git a/hammer.mjs b/hammer.mjs index 2e02a034e..6caa711fb 100644 --- a/hammer.mjs +++ b/hammer.mjs @@ -1,17 +1,18 @@ -import { compression, measurement } from './benchmark' -import { readFileSync } from 'fs' +import { compression, measurement } from './task/benchmark' +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 examples/index.ts') } // ------------------------------------------------------------------------------- // Start @@ -57,19 +58,31 @@ export async function test(filter = '') { // Build // ------------------------------------------------------------------------------- export async function build(target = 'target/build') { - await test() await folder(target).delete() - await shell(`tsc -p ./src/tsconfig.json --outDir ${target}`) + await test() + await shell(`tsc -p ./src/tsconfig.json --outDir ${target} --target ES2020 --module CommonJS --declaration --removeComments`) await folder(target).add('package.json') await folder(target).add('readme.md') await folder(target).add('license') await shell(`cd ${target} && npm pack`) } +// ------------------------------------------------------------------------------- +// Install +// ------------------------------------------------------------------------------- +export async function install_local(target = 'target/typebox') { + await clean() + await build(target) + await folder('node_modules').add(target) + const packagePath = `${target}/package.json` + const packageJson = JSON.parse(Fs.readFileSync(packagePath, 'utf-8')) + packageJson.name = 'typebox' + Fs.writeFileSync(packagePath, JSON.stringify(packageJson, null, 2)) +} // ------------------------------------------------------------- // 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 +92,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/package-lock.json b/package-lock.json index 88740ec7e..c33b6db5e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,17 +1,17 @@ { "name": "@sinclair/typebox", - "version": "0.31.28", + "version": "0.32.0-dev-4", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@sinclair/typebox", - "version": "0.31.28", + "version": "0.32.0-dev-4", "license": "MIT", "devDependencies": { "@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", @@ -54,10 +54,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", @@ -1329,6 +1332,12 @@ "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/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -1470,10 +1479,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", @@ -2284,6 +2296,12 @@ "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 + }, "uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", diff --git a/package.json b/package.json index 064dc20a4..031e67f8c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@sinclair/typebox", - "version": "0.31.28", + "version": "0.32.0-dev-4", "description": "JSONSchema Type Builder with Static Type Resolution for TypeScript", "keywords": [ "typescript", @@ -10,51 +10,40 @@ ], "author": "sinclairzx81", "license": "MIT", - "main": "./typebox.js", - "types": "./typebox.d.ts", + "main": "./index.js", + "types": "./index.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", + "./type": "./type/index.js", "./value": "./value/index.js", - ".": "./typebox.js" + ".": "./index.js" }, "repository": { "type": "git", "url": "https://github.com/sinclairzx81/typebox" }, "scripts": { + "benchmark:compression": "hammer task benchmark_compression", + "benchmark:measurement": "hammer task benchmark_measurement", + "benchmark": "hammer task benchmark", + "install:local": "hammer task install_local", "test:typescript": "hammer task test_typescript", "test:static": "hammer task test_static", "test:runtime": "hammer task test_runtime", + "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": { "@sinclair/hammer": "^0.18.0", "@types/mocha": "^9.1.1", - "@types/node": "^18.11.9", + "@types/node": "^20.10.1", "ajv": "^8.12.0", "ajv-formats": "^2.1.1", "mocha": "^9.2.2", diff --git a/readme.md b/readme.md index 8ba350169..803c72408 100644 --- a/readme.md +++ b/readme.md @@ -25,16 +25,10 @@ $ 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', @@ -90,6 +84,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,8 +103,6 @@ License MIT - [Ajv](#typecheck-ajv) - [TypeCompiler](#typecheck-typecompiler) - [TypeSystem](#typesystem) - - [Types](#typesystem-types) - - [Formats](#typesystem-formats) - [Errors](#typesystem-errors) - [Policies](#typesystem-policies) - [Workbench](#workbench) @@ -127,7 +121,7 @@ License MIT The following shows general usage. ```typescript -import { Type, Static } from '@sinclair/typebox' +import { Type, type Static } from '@sinclair/typebox' //-------------------------------------------------------------------------------------------- // @@ -297,6 +291,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', │ @@ -697,7 +707,7 @@ Object properties can be modified with Readonly and Optional. The following tabl Generic types can be created with generic functions. All types extend the base type TSchema. It is common to constrain generic function arguments to this type. The following creates a generic Vector type. ```typescript -import { Type, Static, TSchema } from '@sinclair/typebox' +import { Type, type Static, type TSchema } from '@sinclair/typebox' const Vector = (t: T) => Type.Object({ x: t, y: t, z: t }) @@ -737,20 +747,65 @@ type T = Static // type T = string | null ### Reference Types -Reference types are supported with Ref. +Reference types can be created with Type.Ref. These types infer the same as the target type. ```typescript -const T = Type.String({ $id: 'T' }) // const T = { - // $id: 'T', - // type: 'string' - // } +const Vector = Type.Object({ // const Vector = { + x: Type.Number(), // type: 'object', + y: Type.Number(), // required: ['x', 'y', 'z'], +}, { $id: 'Vector' }) // properties: { + // x: { type: 'number' }, + // y: { type: 'number' } + // }, + // $id: 'Vector' + // } -const R = Type.Ref('T') // const R = { - // $ref: 'T' +const VectorRef = Type.Ref(Vector) // const VectorRef = { + // $ref: 'Vector' // } -type R = Static // type R = string +type VectorRef = Static // type VectorRef = { + // x: number, + // y: number + // } ``` +Use Type.Deref to dereference a type. This type will recursively reconstruct interior referenced types. +```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 reference types do not contain structural information about the type they're referencing. This means they cannot be used directly with some type mapping types (such as Partial) that require structural information to operate. For applications that need mapping on references, use Type.Deref to dereference the type first. @@ -793,88 +848,80 @@ 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. +TypeBox supports runtime conditional types with Extends. This type performs a structural assignability check for the first two arguments and will infer one of the second two arguments based on the result. The Extends type is designed to match the assignability rules of TypeScript conditional types. The conditional derived types Exclude and Extract are also supported. ```typescript -// TypeScript - -type T0 = string extends number ? true : false // type T0 = false +// Extends -type T1 = Extract<(1 | 2 | 3), 1> // type T1 = 1 +type T = string extends number ? true : false // type T = false -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), // } +const T = Type.Extends( // const T: TLiteral + Type.String(), + Type.Number(), + 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), // } +// Extract + +type T = Extract<1 | 2 | 3, 1> // type T = 1 + +const T = Type.Extract( // const T: TLiteral<1> + Type.Union([ + Type.Literal(1), + Type.Literal(2), Type.Literal(3) ]), Type.Literal(1) ) -const T2 = Type.Exclude( // const T2: TUnion<[ +// Exclude + +type T = Exclude<1 | 2 | 3, 1> // type T = 2 | 3 + +const T = Type.Exclude( // const T: 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 - // }] - // } + Type.Literal(2), // ]> + Type.Literal(3) + ]), + Type.Literal(1) +) ``` ### 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. TypeBox encodes template literals as regular expression string patterns which enable the template be checked via Json Schema. It also supports regular expression parsing, enabling 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, + // }> ``` @@ -884,32 +931,24 @@ const R = Type.Record(T, Type.String()) // const R = { TypeBox supports Indexed Access Types with Index. This type enables uniform access to interior property and array element types without having to extract them from the underlying schema representation. This type is supported for Object, Array, Tuple, Union and Intersect types. ```typescript -const T = Type.Object({ // const T = { - 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({ // const T: TObject<{ + x: Type.Number(), // x: TNumber, + y: Type.String(), // y: TString, + z: Type.Boolean() // z: TBoolean +}) // }> -const A = Type.Index(T, ['x']) // const A = { type: 'number' } +const A = Type.Index(T, ['x']) // const A: TNumber -const B = Type.Index(T, ['x', 'y']) // const B = { - // anyOf: [ - // { type: 'number' }, - // { type: 'string' } - // ] - // } +const B = Type.Index(T, ['x', 'y']) // 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)) // const C: TUnion<[ + // TNumber, + // TString, + // TBoolean + // ]> ``` @@ -919,28 +958,17 @@ const C = Type.Index(T, Type.KeyOf(T)) // const C = { 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 = { - Type.String(), // type: 'array', - Type.Number() // items: [ -]) // { type: 'string' }, - // { type: 'number' } - // ], - // additionalItems: false, - // minItems: 2, - // maxItems: 2, - // } +const T = Type.Tuple([ // const T: TTuple<[ + Type.String(), // TString, + Type.Number() // TNumber +]) // ]> -const R = Type.Rest(T) // const R = [ - // { type: 'string' }, - // { type: 'number' } - // ] +const R = Type.Rest(T) // const R: [TString, TNumber] -const U = Type.Union(R) // const U = { - // anyOf: [ - // { type: 'string' }, - // { type: 'number' } - // ] - // } +const U = Type.Union(R) // const T: TUnion<[ + // TString, + // TNumber + // ]> ``` @@ -1042,12 +1070,12 @@ 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. ```typescript -import { Type, Kind, TypeGuard } from '@sinclair/typebox' +import { Type, TypeGuard } from '@sinclair/typebox' const A: unknown = { ... } @@ -1144,6 +1172,44 @@ 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 + +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. + +```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 + +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. + +```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 @@ -1311,7 +1377,7 @@ The TypeBox type system can be extended with additional types and formats using Use the TypeRegistry to register a new type. The Kind must match the registered type name. ```typescript -import { TypeRegistry, Kind } from '@sinclair/typebox' +import { TypeRegistry, Symbols } from '@sinclair/typebox' TypeRegistry.Set('Foo', (schema, value) => value === 'foo') @@ -1461,44 +1527,6 @@ const C = TypeCompiler.Code(Type.String()) // const C = `return functi 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 @@ -1627,7 +1655,7 @@ 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. You can review this benchmark [here](https://github.com/sinclairzx81/typebox/blob/master/build/benchmark/measurement/module/compile.ts). ```typescript ┌────────────────────────────┬────────────┬──────────────┬──────────────┬──────────────┐ @@ -1669,7 +1697,7 @@ 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. You can review this benchmark [here](https://github.com/sinclairzx81/typebox/blob/master/build/benchmark/measurement/module/check.ts). ```typescript ┌────────────────────────────┬────────────┬──────────────┬──────────────┬──────────────┬──────────────┐ @@ -1719,11 +1747,12 @@ 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 │ '109.3 kb' │ ' 48.5 kb' │ '2.25 x' │ +│ typebox/errors │ ' 54.8 kb' │ ' 24.9 kb' │ '2.20 x' │ +│ typebox/system │ ' 12.1 kb' │ ' 6.1 kb' │ '1.98 x' │ +│ typebox/type │ ' 77.8 kb' │ ' 32.8 kb' │ '2.37 x' │ +│ typebox/value │ '143.7 kb' │ ' 61.2 kb' │ '2.35 x' │ +│ typebox │ ' 77.9 kb' │ ' 32.8 kb' │ '2.37 x' │ └──────────────────────┴────────────┴────────────┴─────────────┘ ``` diff --git a/src/compiler/compiler.ts b/src/compiler/compiler.ts index fe03f6d2e..6f7805709 100644 --- a/src/compiler/compiler.ts +++ b/src/compiler/compiler.ts @@ -26,25 +26,63 @@ 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 { Encode as TransformEncode, Decode as TransformDecode, HasTransform, TransformDecodeCheckError, TransformEncodeCheckError } from '../value/transform/index' +import { IsArray, IsString, IsNumber, IsBigInt } from '../value/guard/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 { Deref } from '../value/deref/index' +import { Hash } from '../value/hash/index' +import { Kind } from '../type/symbols/index' -// ------------------------------------------------------------------- +import { TSchema as IsSchemaType } from '../type/guard/type' +import { TypeRegistry, FormatRegistry } from '../type/registry/index' +import { KeyOfStringResolvePattern } 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 { 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' + +// ------------------------------------------------------------------ // 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 +93,30 @@ 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 { + /** + * Decodes a value or throws if error + * @deprecated Use raw `Decode` from `Value` module + */ + 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 - } - /** 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 + return this.hasTransform ? TransformDecode(this.schema, this.references, value) : value + } + /** + * Encodes a value or throws if error + * @deprecated Use raw `Encode` from `Value` module + */ + 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 +131,9 @@ namespace Character { return code >= 48 && code <= 57 } } -// ------------------------------------------------------------------- +// ------------------------------------------------------------------ // MemberExpression -// ------------------------------------------------------------------- +// ------------------------------------------------------------------ namespace MemberExpression { function IsFirstCharacterNumeric(value: string) { if (value.length === 0) return false @@ -111,9 +155,9 @@ namespace MemberExpression { return IsAccessor(key) ? `${object}.${key}` : `${object}['${EscapeHyphen(key)}']` } } -// ------------------------------------------------------------------- +// ------------------------------------------------------------------ // Identifier -// ------------------------------------------------------------------- +// ------------------------------------------------------------------ namespace Identifier { export function Encode($id: string) { const buffer: string[] = [] @@ -128,30 +172,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 Error { + constructor(public readonly schema: TSchema) { super('Unknown type') } } -export class TypeCompilerTypeGuardError extends Types.TypeBoxError { - constructor(public readonly schema: Types.TSchema) { +export class TypeCompilerTypeGuardError extends Error { + 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 +215,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* TAny(schema: TAny, references: TSchema[], value: string): IterableIterator { yield 'true' } - function* TArray(schema: Types.TArray, references: Types.TSchema[], value: string): IterableIterator { + function* TArray(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 (IsSchemaType(schema.contains) || IsNumber(schema.minContains) || IsNumber(schema.maxContains)) { + const containsSchema = IsSchemaType(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 +258,10 @@ export namespace TypeCompiler { yield `((${parameter}) => { ${block} )(${value})` } } - function* TAsyncIterator(schema: Types.TAsyncIterator, references: Types.TSchema[], value: string): IterableIterator { + function* TAsyncIterator(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* TBigInt(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 +269,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* TBoolean(schema: TBoolean, references: TSchema[], value: string): IterableIterator { yield `(typeof ${value} === 'boolean')` } - function* TConstructor(schema: Types.TConstructor, references: Types.TSchema[], value: string): IterableIterator { + function* TConstructor(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* TDate(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 +283,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* TFunction(schema: TFunction, references: TSchema[], value: string): IterableIterator { yield `(typeof ${value} === 'function')` } - function* TInteger(schema: Types.TInteger, references: Types.TSchema[], value: string): IterableIterator { + function* TInteger(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 +294,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* TIntersect(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(KeyOfStringResolvePattern(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 (IsSchemaType(schema.unevaluatedProperties)) { + const keyCheck = CreateVariable(`${new RegExp(KeyOfStringResolvePattern(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* TIterator(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* TLiteral(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* TNever(schema: TNever, references: TSchema[], value: string): IterableIterator { yield `false` } - function* TNot(schema: Types.TNot, references: Types.TSchema[], value: string): IterableIterator { + function* TNot(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* TNull(schema: TNull, references: TSchema[], value: string): IterableIterator { yield `(${value} === null)` } - function* TNumber(schema: Types.TNumber, references: Types.TSchema[], value: string): IterableIterator { + function* TNumber(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 +336,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* TObject(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 +346,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 +366,28 @@ 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* TPromise(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* TRecord(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 = IsSchemaType(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* TRef(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* TString(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 +399,19 @@ export namespace TypeCompiler { yield `format('${schema.format}', ${value})` } } - function* TSymbol(schema: Types.TSymbol, references: Types.TSchema[], value: string): IterableIterator { + function* TSymbol(schema: TSymbol, references: TSchema[], value: string): IterableIterator { yield `(typeof ${value} === 'symbol')` } - function* TTemplateLiteral(schema: Types.TTemplateLiteral, references: Types.TSchema[], value: string): IterableIterator { + function* TTemplateLiteral(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* TThis(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* TTuple(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 +420,35 @@ export namespace TypeCompiler { yield `${expression}` } } - function* TUndefined(schema: Types.TUndefined, references: Types.TSchema[], value: string): IterableIterator { + function* TUndefined(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* TUnion(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* TUint8Array(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* TUnknown(schema: TUnknown, references: TSchema[], value: string): IterableIterator { yield 'true' } - function* TVoid(schema: Types.TVoid, references: Types.TSchema[], value: string): IterableIterator { + function* TVoid(schema: TVoid, references: TSchema[], value: string): IterableIterator { yield Policy.IsVoidLike(value) } - function* TKind(schema: Types.TSchema, references: Types.TSchema[], value: string): IterableIterator { + function* TKind(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: T, 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,7 +459,7 @@ export namespace TypeCompiler { return yield `${functionName}(${value})` } } - switch (schema_[Types.Kind]) { + switch (schema_[Kind]) { case 'Any': return yield* TAny(schema_, references_, value) case 'Array': @@ -477,24 +521,24 @@ export namespace TypeCompiler { case 'Void': return yield* TVoid(schema_, references_, value) default: - if (!Types.TypeRegistry.Has(schema_[Types.Kind])) throw new TypeCompilerUnknownTypeError(schema) + if (!TypeRegistry.Has(schema_[Kind])) throw new TypeCompilerUnknownTypeError(schema) return yield* TKind(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 +549,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 +563,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 +579,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 +598,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 (!IsSchemaType(schema)) throw new TypeCompilerTypeGuardError(schema) + for (const schema of references) if (!IsSchemaType(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/errors/errors.ts b/src/errors/errors.ts index 9f09e1f55..20198717e 100644 --- a/src/errors/errors.ts +++ b/src/errors/errors.ts @@ -26,11 +26,46 @@ 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 { IsArray, IsUint8Array, IsDate, IsPromise, IsFunction, IsAsyncIterator, IsIterator, IsBoolean, IsNumber, IsBigInt, IsString, IsSymbol, IsInteger, IsNull, IsUndefined } from '../value/guard/index' +import { TypeSystemPolicy, TypeSystemErrorFunction } from '../system/index' +import { KeyOfStringResolvePattern } from '../type/keyof/index' +import { TypeRegistry, FormatRegistry } from '../type/registry/index' +import { ExtendsUndefinedCheck } from '../type/extends/extends-undefined' +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 { 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' // -------------------------------------------------------------------------- // ValueErrorType @@ -105,7 +140,7 @@ export enum ValueErrorType { // -------------------------------------------------------------------------- export interface ValueError { type: ValueErrorType - schema: Types.TSchema + schema: TSchema path: string value: unknown message: string @@ -113,8 +148,8 @@ export interface ValueError { // -------------------------------------------------------------------------- // ValueErrors // -------------------------------------------------------------------------- -export class ValueErrorsUnknownTypeError extends Types.TypeBoxError { - constructor(public readonly schema: Types.TSchema) { +export class ValueErrorsUnknownTypeError extends Error { + constructor(public readonly schema: TSchema) { super('Unknown type') } } @@ -147,14 +182,14 @@ export class ValueErrorIterator { // -------------------------------------------------------------------------- // Create // -------------------------------------------------------------------------- -function Create(type: ValueErrorType, schema: Types.TSchema, path: string, value: unknown): ValueError { +function Create(type: ValueErrorType, schema: TSchema, path: string, value: unknown): ValueError { return { type, schema, path, value, message: TypeSystemErrorFunction.Get()(schema, type) } } // -------------------------------------------------------------------------- // Types // -------------------------------------------------------------------------- -function* TAny(schema: Types.TAny, references: Types.TSchema[], path: string, value: any): IterableIterator {} -function* TArray(schema: Types.TArray, references: Types.TSchema[], path: string, value: any): IterableIterator { +function* TAny(schema: TAny, references: TSchema[], path: string, value: any): IterableIterator {} +function* TArray(schema: TArray, references: TSchema[], path: string, value: any): IterableIterator { if (!IsArray(value)) { return yield Create(ValueErrorType.Array, schema, path, value) } @@ -175,7 +210,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 +222,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* TAsyncIterator(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* TBigInt(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 +243,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* TBoolean(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* TConstructor(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* TDate(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 +267,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* TFunction(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* TInteger(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 +288,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* TIntersect(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 +297,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(KeyOfStringResolvePattern(schema)) for (const valueKey of Object.getOwnPropertyNames(value)) { if (!keyCheck.test(valueKey)) { yield Create(ValueErrorType.IntersectUnevaluatedProperties, schema, `${path}/${valueKey}`, value) @@ -270,7 +305,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(KeyOfStringResolvePattern(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 +314,22 @@ function* TIntersect(schema: Types.TIntersect, references: Types.TSchema[], path } } } -function* TIterator(schema: Types.TIterator, references: Types.TSchema[], path: string, value: any): IterableIterator { +function* TIterator(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* TLiteral(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* TNever(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* TNot(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* TNull(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* TNumber(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 +347,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* TObject(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 +372,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 +389,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* TPromise(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* TRecord(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 +407,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 +417,10 @@ function* TRecord(schema: Types.TRecord, references: Types.TSchema[], path: stri } } } -function* TRef(schema: Types.TRef, references: Types.TSchema[], path: string, value: any): IterableIterator { +function* TRef(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* TString(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 +435,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* TSymbol(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* TTemplateLiteral(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* TThis(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* TTuple(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 +473,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* TUndefined(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* TUnion(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 +487,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* TUint8Array(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,18 +496,18 @@ 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* TUnknown(schema: TUnknown, references: TSchema[], path: string, value: any): IterableIterator {} +function* TVoid(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* TKind(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) case 'Array': @@ -534,14 +569,14 @@ function* Visit(schema: T, references: Types.TSchema[], case 'Void': return yield* TVoid(schema_, references_, path, value) default: - if (!Types.TypeRegistry.Has(schema_[Types.Kind])) throw new ValueErrorsUnknownTypeError(schema) + if (!TypeRegistry.Has(schema_[Kind])) throw new ValueErrorsUnknownTypeError(schema) return yield* TKind(schema_, references_, path, value) } } /** Returns an iterator for each error in this value. */ -export function Errors(schema: T, references: Types.TSchema[], value: unknown): ValueErrorIterator +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/index.ts b/src/index.ts new file mode 100644 index 000000000..8a8143e17 --- /dev/null +++ b/src/index.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. + +---------------------------------------------------------------------------*/ + +// ------------------------------------------------------------------ +// 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 { Clone } from './type/clone/value' + +// ------------------------------------------------------------------ +// 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, 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 } from './type/exclude/index' +export { Extends, type TExtends, ExtendsCheck, ExtendsResult, ExtendsUndefinedCheck } from './type/extends/index' +export { Extract, type TExtract } 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, type TIndex, IndexedKeyResolve, IndexedTypeResolve } 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, type TIntersect, type IntersectOptions } from './type/intersect/index' +export { Iterator, type TIterator } from './type/iterator/index' +export { Capitalize, type TCapitalize, Lowercase, type TLowercase, Uncapitalize, type TUncapitalize, Uppercase, type TUppercase, IntrinsicResolve } from './type/intrinsic/index' +export { KeyOf, type TKeyOf, KeyOfStringResolve, KeyOfStringResolvePattern, KeyOfTypeResolve } from './type/keyof/index' +export { Literal, type TLiteral } from './type/literal/index' +export { Mapped, type TMappedKey, type MappedFunction, type TMappedResult } 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 } from './type/omit/index' +export { Optional, type TOptional } from './type/optional/index' +export { Parameters, type TParameters } from './type/parameters/index' +export { Partial, type TPartial } from './type/partial/index' +export { Pick, type TPick } from './type/pick/index' +export { Promise, type TPromise } from './type/promise/index' +export { Readonly, type TReadonly } 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 } 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 SymbolValue } from './type/symbol/index' +export { TemplateLiteral, IsTemplateLiteralFinite, TemplateLiteralParse, TemplateLiteralParseExact, TemplateLiteralGenerate, type TTemplateLiteral, type TTemplateLiteralKind } from './type/template-literal/index' +export { Transform, type TTransform, type TransformOptions, type TransformFunction, TransformDecodeBuilder, TransformEncodeBuilder } 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, type TUnion } 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 { JsonTypeBuilder, JavaScriptTypeBuilder } from './type/type/index' +import { Type } from './type/type/index' +export { Type } +export default Type diff --git a/src/system/errors.ts b/src/system/errors.ts new file mode 100644 index 000000000..ab8439906 --- /dev/null +++ b/src/system/errors.ts @@ -0,0 +1,191 @@ +/*-------------------------------------------------------------------------- + +@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 { ValueErrorType } from '../errors/errors' +import { TSchema } from '../type/schema/index' +import { Kind } from '../type/symbols/index' + +// ------------------------------------------------------------------ +// ErrorFunction +// ------------------------------------------------------------------ +export type ErrorFunction = (schema: TSchema, type: ValueErrorType) => string +// ------------------------------------------------------------------ +// 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 + } +} +// ------------------------------------------------------------------ +// DefaultErrorFunction +// ------------------------------------------------------------------ +/** Creates an error message using en-US as the default locale */ +export function DefaultErrorFunction(schema: 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[Kind]}'` + default: + return 'Unknown error type' + } +} diff --git a/src/system/index.ts b/src/system/index.ts index 21c978add..ead7f9713 100644 --- a/src/system/index.ts +++ b/src/system/index.ts @@ -27,4 +27,6 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ export { ValueErrorType } from '../errors/errors' -export * from './system' +export { DefaultErrorFunction, ErrorFunction, TypeSystemErrorFunction } from './errors' +export { TypeSystemPolicy } from './policy' +export { TypeSystem, TypeSystemDuplicateFormat, TypeSystemDuplicateTypeKind } from './system' diff --git a/src/system/policy.ts b/src/system/policy.ts new file mode 100644 index 000000000..ffb7ba772 --- /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' + +// ------------------------------------------------------------------ +// 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 + } +} diff --git a/src/system/system.ts b/src/system/system.ts index 528286259..91acedeec 100644 --- a/src/system/system.ts +++ b/src/system/system.ts @@ -26,234 +26,38 @@ 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' -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ // Errors -// -------------------------------------------------------------------------- -export class TypeSystemDuplicateTypeKind extends Types.TypeBoxError { +// ------------------------------------------------------------------ +export class TypeSystemDuplicateTypeKind extends Error { constructor(kind: string) { super(`Duplicate type kind '${kind}' detected`) } } -export class TypeSystemDuplicateFormat extends Types.TypeBoxError { +export class TypeSystemDuplicateFormat extends Error { constructor(kind: string) { super(`Duplicate string format '${kind}' detected`) } } -// ------------------------------------------------------------------------------------------- +// ------------------------------------------------------------------ // TypeSystem -// ------------------------------------------------------------------------------------------- +// ------------------------------------------------------------------ /** Creates user defined types and formats and provides overrides for value checking behaviours */ export namespace TypeSystem { /** Creates a new type */ export function Type>(kind: string, check: (options: Options, value: unknown) => boolean) { - if (Types.TypeRegistry.Has(kind)) throw new TypeSystemDuplicateTypeKind(kind) - Types.TypeRegistry.Set(kind, check) - return (options: Partial = {}) => Types.Type.Unsafe({ ...options, [Types.Kind]: kind }) + if (TypeRegistry.Has(kind)) throw new TypeSystemDuplicateTypeKind(kind) + TypeRegistry.Set(kind, check) + return (options: Partial = {}) => Unsafe({ ...options, [Kind]: kind }) } /** Creates a new string format */ export function Format(format: F, check: (value: string) => boolean): F { - if (Types.FormatRegistry.Has(format)) throw new TypeSystemDuplicateFormat(format) - Types.FormatRegistry.Set(format, check) + if (FormatRegistry.Has(format)) throw new TypeSystemDuplicateFormat(format) + FormatRegistry.Set(format, check) return format } } -// -------------------------------------------------------------------------- -// TypeSystemErrorFunction -// -------------------------------------------------------------------------- -/** Manages error message providers */ -export namespace TypeSystemErrorFunction { - let errorMessageFunction: ErrorFunction = DefaultErrorFunction - /** Resets the error message function to en-us */ - export function Reset() { - errorMessageFunction = DefaultErrorFunction - } - /** Sets the error message function used to generate error messages */ - export function Set(callback: ErrorFunction) { - errorMessageFunction = callback - } - /** Gets the error message function */ - export function Get(): ErrorFunction { - return errorMessageFunction - } -} -// -------------------------------------------------------------------------- -// TypeSystemPolicy -// -------------------------------------------------------------------------- -/** Shared assertion routines used by the value and errors modules */ -export namespace TypeSystemPolicy { - /** Sets whether TypeBox should assert optional properties using the TypeScript `exactOptionalPropertyTypes` assertion policy. The default is `false` */ - export let ExactOptionalPropertyTypes: boolean = false - /** Sets whether arrays should be treated as a kind of objects. The default is `false` */ - export let AllowArrayObject: boolean = false - /** Sets whether `NaN` or `Infinity` should be treated as valid numeric values. The default is `false` */ - export let AllowNaN: boolean = false - /** Sets whether `null` should validate for void types. The default is `false` */ - export let AllowNullVoid: boolean = false - /** Asserts this value using the ExactOptionalPropertyTypes policy */ - export function IsExactOptionalProperty(value: Record, key: string) { - return ExactOptionalPropertyTypes ? key in value : value[key] !== undefined - } - /** Asserts this value using the AllowArrayObjects policy */ - export function IsObjectLike(value: unknown): value is Record { - const isObject = IsObject(value) - return AllowArrayObject ? isObject : isObject && !IsArray(value) - } - /** Asserts this value as a record using the AllowArrayObjects policy */ - export function IsRecordLike(value: unknown): value is Record { - return IsObjectLike(value) && !(value instanceof Date) && !(value instanceof Uint8Array) - } - /** Asserts this value using the AllowNaN policy */ - export function IsNumberLike(value: unknown): value is number { - const isNumber = IsNumber(value) - return AllowNaN ? isNumber : isNumber && Number.isFinite(value) - } - /** Asserts this value using the AllowVoidNull policy */ - export function IsVoidLike(value: unknown): value is void { - const isUndefined = IsUndefined(value) - return AllowNullVoid ? isUndefined || value === null : isUndefined - } -} -// -------------------------------------------------------------------------- -// ErrorFunction -// -------------------------------------------------------------------------- -export type ErrorFunction = (schema: Types.TSchema, type: ValueErrorType) => string -// -------------------------------------------------------------------------- -// DefaultErrorFunction -// -------------------------------------------------------------------------- -/** Creates an error message using en-US as the default locale */ -export function DefaultErrorFunction(schema: Types.TSchema, errorType: ValueErrorType) { - switch (errorType) { - case ValueErrorType.ArrayContains: - return 'Expected array to contain at least one matching value' - case ValueErrorType.ArrayMaxContains: - return `Expected array to contain no more than ${schema.maxContains} matching values` - case ValueErrorType.ArrayMinContains: - return `Expected array to contain at least ${schema.minContains} matching values` - case ValueErrorType.ArrayMaxItems: - return `Expected array length to be less or equal to ${schema.maxItems}` - case ValueErrorType.ArrayMinItems: - return `Expected array length to be greater or equal to ${schema.minItems}` - case ValueErrorType.ArrayUniqueItems: - return 'Expected array elements to be unique' - case ValueErrorType.Array: - return 'Expected array' - case ValueErrorType.AsyncIterator: - return 'Expected AsyncIterator' - case ValueErrorType.BigIntExclusiveMaximum: - return `Expected bigint to be less than ${schema.exclusiveMaximum}` - case ValueErrorType.BigIntExclusiveMinimum: - return `Expected bigint to be greater than ${schema.exclusiveMinimum}` - case ValueErrorType.BigIntMaximum: - return `Expected bigint to be less or equal to ${schema.maximum}` - case ValueErrorType.BigIntMinimum: - return `Expected bigint to be greater or equal to ${schema.minimum}` - case ValueErrorType.BigIntMultipleOf: - return `Expected bigint to be a multiple of ${schema.multipleOf}` - case ValueErrorType.BigInt: - return 'Expected bigint' - case ValueErrorType.Boolean: - return 'Expected boolean' - case ValueErrorType.DateExclusiveMinimumTimestamp: - return `Expected Date timestamp to be greater than ${schema.exclusiveMinimumTimestamp}` - case ValueErrorType.DateExclusiveMaximumTimestamp: - return `Expected Date timestamp to be less than ${schema.exclusiveMaximumTimestamp}` - case ValueErrorType.DateMinimumTimestamp: - return `Expected Date timestamp to be greater or equal to ${schema.minimumTimestamp}` - case ValueErrorType.DateMaximumTimestamp: - return `Expected Date timestamp to be less or equal to ${schema.maximumTimestamp}` - case ValueErrorType.DateMultipleOfTimestamp: - return `Expected Date timestamp to be a multiple of ${schema.multipleOfTimestamp}` - case ValueErrorType.Date: - return 'Expected Date' - case ValueErrorType.Function: - return 'Expected function' - case ValueErrorType.IntegerExclusiveMaximum: - return `Expected integer to be less than ${schema.exclusiveMaximum}` - case ValueErrorType.IntegerExclusiveMinimum: - return `Expected integer to be greater than ${schema.exclusiveMinimum}` - case ValueErrorType.IntegerMaximum: - return `Expected integer to be less or equal to ${schema.maximum}` - case ValueErrorType.IntegerMinimum: - return `Expected integer to be greater or equal to ${schema.minimum}` - case ValueErrorType.IntegerMultipleOf: - return `Expected integer to be a multiple of ${schema.multipleOf}` - case ValueErrorType.Integer: - return 'Expected integer' - case ValueErrorType.IntersectUnevaluatedProperties: - return 'Unexpected property' - case ValueErrorType.Intersect: - return 'Expected all values to match' - case ValueErrorType.Iterator: - return 'Expected Iterator' - case ValueErrorType.Literal: - return `Expected ${typeof schema.const === 'string' ? `'${schema.const}'` : schema.const}` - case ValueErrorType.Never: - return 'Never' - case ValueErrorType.Not: - return 'Value should not match' - case ValueErrorType.Null: - return 'Expected null' - case ValueErrorType.NumberExclusiveMaximum: - return `Expected number to be less than ${schema.exclusiveMaximum}` - case ValueErrorType.NumberExclusiveMinimum: - return `Expected number to be greater than ${schema.exclusiveMinimum}` - case ValueErrorType.NumberMaximum: - return `Expected number to be less or equal to ${schema.maximum}` - case ValueErrorType.NumberMinimum: - return `Expected number to be greater or equal to ${schema.minimum}` - case ValueErrorType.NumberMultipleOf: - return `Expected number to be a multiple of ${schema.multipleOf}` - case ValueErrorType.Number: - return 'Expected number' - case ValueErrorType.Object: - return 'Expected object' - case ValueErrorType.ObjectAdditionalProperties: - return 'Unexpected property' - case ValueErrorType.ObjectMaxProperties: - return `Expected object to have no more than ${schema.maxProperties} properties` - case ValueErrorType.ObjectMinProperties: - return `Expected object to have at least ${schema.minProperties} properties` - case ValueErrorType.ObjectRequiredProperty: - return 'Required property' - case ValueErrorType.Promise: - return 'Expected Promise' - case ValueErrorType.StringFormatUnknown: - return `Unknown format '${schema.format}'` - case ValueErrorType.StringFormat: - return `Expected string to match '${schema.format}' format` - case ValueErrorType.StringMaxLength: - return `Expected string length less or equal to ${schema.maxLength}` - case ValueErrorType.StringMinLength: - return `Expected string length greater or equal to ${schema.minLength}` - case ValueErrorType.StringPattern: - return `Expected string to match '${schema.pattern}'` - case ValueErrorType.String: - return 'Expected string' - case ValueErrorType.Symbol: - return 'Expected symbol' - case ValueErrorType.TupleLength: - return `Expected tuple to have ${schema.maxItems || 0} elements` - case ValueErrorType.Tuple: - return 'Expected tuple' - case ValueErrorType.Uint8ArrayMaxByteLength: - return `Expected byte length less or equal to ${schema.maxByteLength}` - case ValueErrorType.Uint8ArrayMinByteLength: - return `Expected byte length greater or equal to ${schema.minByteLength}` - case ValueErrorType.Uint8Array: - return 'Expected Uint8Array' - case ValueErrorType.Undefined: - return 'Expected undefined' - case ValueErrorType.Union: - return 'Expected union value' - case ValueErrorType.Void: - return 'Expected void' - case ValueErrorType.Kind: - return `Expected kind '${schema[Types.Kind]}'` - default: - return 'Unknown error type' - } -} diff --git a/src/tsconfig.json b/src/tsconfig.json index c78307cfa..e53e3ebf1 100644 --- a/src/tsconfig.json +++ b/src/tsconfig.json @@ -1,4 +1,4 @@ { "extends": "../tsconfig.json", - "files": ["compiler/index.ts", "errors/index.ts", "system/index.ts", "value/index.ts", "typebox.ts"] + "files": ["compiler/index.ts", "errors/index.ts", "system/index.ts", "type/index.ts", "value/index.ts", "index.ts"] } diff --git a/src/type/any/any.ts b/src/type/any/any.ts new file mode 100644 index 000000000..c5b166f80 --- /dev/null +++ b/src/type/any/any.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 { Kind } from '../symbols/index' + +// ------------------------------------------------------------------ +// TAny +// ------------------------------------------------------------------ +export interface TAny extends TSchema { + [Kind]: 'Any' + static: any +} + +/** `[Json]` Creates an Any type */ +export function Any(options: SchemaOptions = {}): TAny { + return { ...options, [Kind]: 'Any' } as unknown as TAny +} diff --git a/src/type/any/index.ts b/src/type/any/index.ts new file mode 100644 index 000000000..29688e122 --- /dev/null +++ b/src/type/any/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './any' diff --git a/src/type/array/array.ts b/src/type/array/array.ts new file mode 100644 index 000000000..02c0f0375 --- /dev/null +++ b/src/type/array/array.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, SchemaOptions } from '../schema/index' +import type { Static } from '../static/index' +import { Kind } from '../symbols/index' +import { CloneType } from '../clone/type' + +// ------------------------------------------------------------------ +// 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 +} +/** `[Json]` Creates an Array type */ +export function Array(schema: T, options: ArrayOptions = {}): TArray { + return { + ...options, + [Kind]: 'Array', + type: 'array', + items: CloneType(schema), + } as unknown as TArray +} diff --git a/src/type/array/index.ts b/src/type/array/index.ts new file mode 100644 index 000000000..ae02b63c0 --- /dev/null +++ b/src/type/array/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './array' diff --git a/src/type/async-iterator/async-iterator.ts b/src/type/async-iterator/async-iterator.ts new file mode 100644 index 000000000..476057f13 --- /dev/null +++ b/src/type/async-iterator/async-iterator.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 type { Static } from '../static/index' +import { Kind } from '../symbols/index' +import { CloneType } from '../clone/type' + +// ------------------------------------------------------------------ +// TAsyncIterator +// ------------------------------------------------------------------ +export interface TAsyncIterator extends TSchema { + [Kind]: 'AsyncIterator' + static: AsyncIterableIterator> + type: 'AsyncIterator' + items: T +} +/** `[JavaScript]` Creates a AsyncIterator type */ +export function AsyncIterator(items: T, options: SchemaOptions = {}): TAsyncIterator { + return { + ...options, + [Kind]: 'AsyncIterator', + type: 'AsyncIterator', + items: CloneType(items), + } as unknown as TAsyncIterator +} diff --git a/src/type/async-iterator/index.ts b/src/type/async-iterator/index.ts new file mode 100644 index 000000000..d6bc7cb22 --- /dev/null +++ b/src/type/async-iterator/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './async-iterator' diff --git a/src/type/awaited/awaited.ts b/src/type/awaited/awaited.ts new file mode 100644 index 000000000..16a55c972 --- /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 { type TIntersect, Intersect } from '../intersect/index' +import { type TUnion, Union } from '../union/index' +import { type TPromise } from '../promise/index' +import { CloneType } from '../clone/type' +import { TIntersect as IsIntersectType, TUnion as IsUnionType, TPromise as IsPromiseType } from '../guard/type' + +// ------------------------------------------------------------------ +// AwaitedResolve +// ------------------------------------------------------------------ +// prettier-ignore +type AwaitedUnwrap = + T extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? [AwaitedResolve, ...AwaitedUnwrap] + : [] +// prettier-ignore +function AwaitedUnwrap(T: [...T]) : AwaitedUnwrap { + const [L, ...R] = T + return ( + T.length > 0 + ? [Resolve(L), ...AwaitedUnwrap(R)] + : [] + ) as AwaitedUnwrap +} +// ---------------------------------------------------------------- +// IntersectRest +// ---------------------------------------------------------------- +// prettier-ignore +type AwaitedIntersectRest = TIntersect> +// prettier-ignore +function AwaitedIntersectRest(T: [...T]): AwaitedIntersectRest { + return Intersect(AwaitedUnwrap(T) as TSchema[]) as unknown as AwaitedIntersectRest +} +// ---------------------------------------------------------------- +// UnionRest +// ---------------------------------------------------------------- +// prettier-ignore +type AwaitedUnionRest = TUnion> +// prettier-ignore +function AwaitedUnionRest(T: [...T]): AwaitedUnionRest { + return Union(AwaitedUnwrap(T) as TSchema[]) as unknown as AwaitedUnionRest +} +// ---------------------------------------------------------------- +// Promise +// ---------------------------------------------------------------- +type AwaitedPromise = AwaitedResolve +// prettier-ignore +function AwaitedPromise(T: T): AwaitedPromise { + return Resolve(T) as AwaitedPromise +} +// ---------------------------------------------------------------- +// Resolve +// ---------------------------------------------------------------- +// prettier-ignore +export type AwaitedResolve = + T extends TIntersect ? TIntersect> : + T extends TUnion ? TUnion> : + T extends TPromise ? AwaitedResolve : + T +// prettier-ignore +export function Resolve(T: T): AwaitedResolve { + return ( + IsIntersectType(T) ? AwaitedIntersectRest(T.allOf) : + IsUnionType(T) ? AwaitedUnionRest(T.anyOf) : + IsPromiseType(T) ? AwaitedPromise(T.item) : + T + ) as AwaitedResolve +} +// ------------------------------------------------------------------ +// TAwaited +// ------------------------------------------------------------------ +export type TAwaited = AwaitedResolve + +/** `[JavaScript]` Constructs a type by recursively unwrapping Promise types */ +export function Awaited(T: T, options: SchemaOptions = {}): AwaitedResolve { + return CloneType(Resolve(T), options) +} diff --git a/src/type/awaited/index.ts b/src/type/awaited/index.ts new file mode 100644 index 000000000..26a23a571 --- /dev/null +++ b/src/type/awaited/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './awaited' diff --git a/src/type/bigint/bigint.ts b/src/type/bigint/bigint.ts new file mode 100644 index 000000000..976397d85 --- /dev/null +++ b/src/type/bigint/bigint.ts @@ -0,0 +1,54 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF 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' + +// ------------------------------------------------------------------ +// TBigInt +// ------------------------------------------------------------------ +export interface BigIntOptions extends SchemaOptions { + exclusiveMaximum?: bigint + exclusiveMinimum?: bigint + maximum?: bigint + minimum?: bigint + multipleOf?: bigint +} +export interface TBigInt extends TSchema, BigIntOptions { + [Kind]: 'BigInt' + static: bigint + type: 'bigint' +} +/** `[JavaScript]` Creates a BigInt type */ +export function BigInt(options: BigIntOptions = {}): TBigInt { + return { + ...options, + [Kind]: 'BigInt', + type: 'bigint', + } as TBigInt +} diff --git a/src/type/bigint/index.ts b/src/type/bigint/index.ts new file mode 100644 index 000000000..ce8f4fbf9 --- /dev/null +++ b/src/type/bigint/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './bigint' diff --git a/src/type/boolean/boolean.ts b/src/type/boolean/boolean.ts new file mode 100644 index 000000000..5516a07f2 --- /dev/null +++ b/src/type/boolean/boolean.ts @@ -0,0 +1,47 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF 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' + +// ------------------------------------------------------------------ +// TBoolean +// ------------------------------------------------------------------ +export interface TBoolean extends TSchema { + [Kind]: 'Boolean' + static: boolean + type: 'boolean' +} +/** `[Json]` Creates a Boolean type */ +export function Boolean(options: SchemaOptions = {}): TBoolean { + return { + ...options, + [Kind]: 'Boolean', + type: 'boolean', + } as unknown as TBoolean +} diff --git a/src/type/boolean/index.ts b/src/type/boolean/index.ts new file mode 100644 index 000000000..aad153cb3 --- /dev/null +++ b/src/type/boolean/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './boolean' diff --git a/src/type/clone/index.ts b/src/type/clone/index.ts new file mode 100644 index 000000000..44bd8cb28 --- /dev/null +++ b/src/type/clone/index.ts @@ -0,0 +1,30 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * as TypeClone from './type' +export * as ValueClone from './value' diff --git a/src/type/clone/type.ts b/src/type/clone/type.ts new file mode 100644 index 000000000..08d1c3f43 --- /dev/null +++ b/src/type/clone/type.ts @@ -0,0 +1,39 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema, SchemaOptions } from '../schema/index' +import { Clone } from './value' + +/** Clones a Rest */ +export function CloneRest(schemas: T): T { + return schemas.map((schema) => CloneType(schema)) as T +} +/** Clones a Type */ +export function CloneType(schema: T, options: SchemaOptions = {}): T { + return { ...Clone(schema), ...options } +} diff --git a/src/type/clone/value.ts b/src/type/clone/value.ts new file mode 100644 index 000000000..0f2ea9450 --- /dev/null +++ b/src/type/clone/value.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 * 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 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.IsObject(value) ? ObjectType(value) : + value + ) +} +/** Clones a value */ +export function Clone(value: T): T { + return Visit(value) +} diff --git a/src/type/composite/composite.ts b/src/type/composite/composite.ts new file mode 100644 index 000000000..545464973 --- /dev/null +++ b/src/type/composite/composite.ts @@ -0,0 +1,72 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF 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 { type TObject, type TProperties, type ObjectOptions, Object } from '../object/index' +import { type TIntersect, Intersect } from '../intersect/index' +import { KeyOfStringResolve } from '../keyof/index' +import { IndexedTypeResolve, Index } from '../indexed/index' +import { CloneType } from '../clone/type' + +// ------------------------------------------------------------------ +// CompositeResolve +// ------------------------------------------------------------------ +// prettier-ignore +type CompositeKeys = + T extends [infer L extends TObject, ...infer R extends TObject[]] + ? keyof L['properties'] | CompositeKeys + : never +// prettier-ignore +type CompositeIndex, K extends string[]> = + K extends [infer L extends string, ...infer R extends string[]] + ? { [_ in L]: IndexedTypeResolve } & CompositeIndex + : {} +// prettier-ignore +type CompositeReduce = UnionToTuple> extends infer K + ? Evaluate, Assert>> + : {} // ^ indexed via intersection of T +// prettier-ignore +export type CompositeResolve = TIntersect extends TIntersect + ? TObject> + : TObject<{}> +export function CompositeResolve(T: [...T]): CompositeResolve { + const intersect: TSchema = Intersect(T, {}) + const keys = KeyOfStringResolve(intersect) as string[] + const properties = keys.reduce((acc, key) => ({ ...acc, [key]: Index(intersect, [key]) }), {} as TProperties) + return Object(properties) as CompositeResolve +} +// ------------------------------------------------------------------ +// TComposite +// ------------------------------------------------------------------ +export type TComposite = CompositeResolve + +/** `[Json]` Creates a Composite object type */ +export function Composite(T: [...T], options?: ObjectOptions): TComposite { + return CloneType(CompositeResolve(T) as TObject, options) as TComposite +} diff --git a/src/type/composite/index.ts b/src/type/composite/index.ts new file mode 100644 index 000000000..2fe294b71 --- /dev/null +++ b/src/type/composite/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './composite' diff --git a/src/type/const/const.ts b/src/type/const/const.ts new file mode 100644 index 000000000..0af825cbd --- /dev/null +++ b/src/type/const/const.ts @@ -0,0 +1,136 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF 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 { IsArray, IsNumber, IsBigInt, IsUint8Array, IsDate, IsIterator, IsObject, IsAsyncIterator, IsFunction, IsUndefined, IsNull, IsSymbol, IsBoolean, IsString } from '../guard/value' +import { Any, type TAny } from '../any/index' +import { BigInt, type TBigInt } from '../bigint/index' +import { Date, type TDate } from '../date/index' +import { Function, 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' + +// ------------------------------------------------------------------ +// FromArray +// ------------------------------------------------------------------ +// prettier-ignore +type FromArray = + T extends readonly [infer L extends unknown, ...infer R extends unknown[]] + ? [FromValue, ...FromArray] + : T +// prettier-ignore +function FromArray(T: [...T]): FromArray { + const [L, ...R] = T + return ( + T.length > 0 + ? [FromValue(L, false) as TSchema, ...FromArray(R)] + : [] + ) as FromArray +} +// ------------------------------------------------------------------ +// FromProperties +// ------------------------------------------------------------------ +// prettier-ignore +type FromProperties> = { + -readonly [K in keyof T]: FromValue extends infer R extends TSchema + ? TReadonly + : TReadonly +} +// prettier-ignore +function FromProperties>(value: T): FromProperties { + return globalThis.Object.getOwnPropertyNames(value).reduce((acc, key) => { + return { ...acc, [key]: Readonly(FromValue(value[key], false)) } + }, {} as TProperties) as unknown as FromProperties +} +// ------------------------------------------------------------------ +// ConditionalReadonly - Only applied if not root +// ------------------------------------------------------------------ +type ConditionalReadonly = Root extends true ? T : TReadonly +function ConditionalReadonly(T: T, root: Root): ConditionalReadonly { + return (root === true ? T : Readonly(T)) as unknown as ConditionalReadonly +} +// ------------------------------------------------------------------ +// FromValue +// ------------------------------------------------------------------ +// prettier-ignore +type FromValue = + T extends AsyncIterableIterator ? ConditionalReadonly : + T extends IterableIterator ? ConditionalReadonly : + T extends readonly unknown[] ? TReadonly>>> : // Always Readonly + T extends Uint8Array ? TUint8Array : + T extends Date ? TDate : + T extends Record ? ConditionalReadonly>>, Root> : + T extends Function ? ConditionalReadonly, 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[])) : // Always Readonly + IsUint8Array(value) ? Uint8Array() : + IsDate(value) ? Date() : + IsObject(value) ? ConditionalReadonly(Object(FromProperties(value as Record) as TProperties), root) : + IsFunction(value) ? ConditionalReadonly(Function([], Unknown()), root) : + IsUndefined(value) ? Undefined() : + IsNull(value) ? Null() : + IsSymbol(value) ? Symbol() : + IsBigInt(value) ? BigInt() : + IsNumber(value) ? Literal(value) : + IsBoolean(value) ? Literal(value) : + IsString(value) ? Literal(value) : + Object({}) + ) as FromValue +} +// ------------------------------------------------------------------ +// TConst +// ------------------------------------------------------------------ +export type TConst = FromValue + +/** `[JavaScript]` Creates a readonly const type from the given value. */ +export function Const(T: T, options: SchemaOptions = {}): TConst { + return TypeClone.CloneType(FromValue(T, true), options) as TConst +} diff --git a/src/type/const/index.ts b/src/type/const/index.ts new file mode 100644 index 000000000..f13d0f549 --- /dev/null +++ b/src/type/const/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './const' diff --git a/src/type/constructor-parameters/constructor-parameters.ts b/src/type/constructor-parameters/constructor-parameters.ts new file mode 100644 index 000000000..1ff7c55cb --- /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 { type TTuple, Tuple } from '../tuple/index' +import { CloneRest } from '../clone/type' + +// ------------------------------------------------------------------ +// ConstructorParameters +// ------------------------------------------------------------------ +// prettier-ignore +export type TConstructorParameters> = ( + Ensure> +) + +/** `[JavaScript]` Extracts the ConstructorParameters from the given Constructor type */ +export function ConstructorParameters>(schema: T, options: SchemaOptions = {}): TConstructorParameters { + return Tuple(CloneRest(schema.parameters), { ...options }) +} diff --git a/src/type/constructor-parameters/index.ts b/src/type/constructor-parameters/index.ts new file mode 100644 index 000000000..23f9c265b --- /dev/null +++ b/src/type/constructor-parameters/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './constructor-parameters' diff --git a/src/type/constructor/constructor.ts b/src/type/constructor/constructor.ts new file mode 100644 index 000000000..3764e5558 --- /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 { Kind } from '../symbols/index' +import { CloneType, CloneRest } from '../clone/type' + +// ------------------------------------------------------------------ +// TConstructorResolve +// ------------------------------------------------------------------ +type TConstructorReturnTypeResolve = Static +// prettier-ignore +type TConstructorParametersResolve = + T extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? [Static, ...TConstructorParametersResolve] + : [] +// prettier-ignore +export type TConstructorResolve = ( + Ensure) => TConstructorReturnTypeResolve> +) +// ------------------------------------------------------------------ +// TConstructor +// ------------------------------------------------------------------ +export interface TConstructor extends TSchema { + [Kind]: 'Constructor' + static: TConstructorResolve + type: 'Constructor' + parameters: T + returns: U +} +/** `[JavaScript]` Creates a Constructor type */ +export function Constructor(parameters: [...T], returns: U, options?: SchemaOptions): TConstructor { + return { + ...options, + [Kind]: 'Constructor', + type: 'Constructor', + parameters: CloneRest(parameters), + returns: CloneType(returns), + } as unknown as TConstructor +} diff --git a/src/type/constructor/index.ts b/src/type/constructor/index.ts new file mode 100644 index 000000000..8e75965f7 --- /dev/null +++ b/src/type/constructor/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './constructor' diff --git a/src/type/date/date.ts b/src/type/date/date.ts new file mode 100644 index 000000000..a4bf5fa5a --- /dev/null +++ b/src/type/date/date.ts @@ -0,0 +1,59 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF 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' + +// ------------------------------------------------------------------ +// 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' +} +/** `[JavaScript]` Creates a Date type */ +export function Date(options: DateOptions = {}): TDate { + return { + ...options, + [Kind]: 'Date', + type: 'Date', + } as unknown as TDate +} diff --git a/src/type/date/index.ts b/src/type/date/index.ts new file mode 100644 index 000000000..932ee5def --- /dev/null +++ b/src/type/date/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './date' diff --git a/src/type/deref/deref.ts b/src/type/deref/deref.ts new file mode 100644 index 000000000..519725af2 --- /dev/null +++ b/src/type/deref/deref.ts @@ -0,0 +1,191 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF 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 } from '../clone/type' +import { Discard } from '../discard/index' +import { IsUndefined } from '../guard/value' + +import { + TConstructor as IsConstructorType, + TFunction as IsFunctionType, + TIntersect as IsIntersectType, + TUnion as IsUnionType, + TTuple as IsTupleType, + TArray as IsArrayType, + TObject as IsObjectType, + TPromise as IsPromiseType, + TAsyncIterator as IsAsyncIteratorType, + TIterator as IsIteratorType, + TRef as IsRefType, +} from '../guard/type' + +// ------------------------------------------------------------------ +// DerefResolve +// ------------------------------------------------------------------ +// prettier-ignore +export type FromRest = ( + T extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? [DerefResolve, ...FromRest] + : [] +) +function FromRest(schema: TSchema[], references: TSchema[]) { + return schema.map((schema) => Deref(schema, references)) +} +// 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[]) { + const clone = CloneType(schema) + clone.parameters = FromRest(clone.parameters, references) + clone.returns = Deref(clone.returns, references) + return clone +} +// prettier-ignore +function FromFunction(schema: TFunction, references: TSchema[]) { + const clone = CloneType(schema) + clone.parameters = FromRest(clone.parameters, references) + clone.returns = Deref(clone.returns, references) + return clone +} +// prettier-ignore +function FromIntersect(schema: TIntersect, references: TSchema[]) { + const clone = CloneType(schema) + clone.allOf = FromRest(clone.allOf, references) + return clone +} +// prettier-ignore +function FromUnion(schema: TUnion, references: TSchema[]) { + const clone = CloneType(schema) + clone.anyOf = FromRest(clone.anyOf, references) + return clone +} +// prettier-ignore +function FromTuple(schema: TTuple, references: TSchema[]) { + const clone = CloneType(schema) + if(IsUndefined(clone.items)) return clone + clone.items = FromRest(clone.items, references) + return clone +} +// prettier-ignore +function FromArray(schema: TArray, references: TSchema[]) { + const clone = CloneType(schema) + clone.items = Deref(clone.items, references) + return clone +} +// prettier-ignore +function FromObject(schema: TObject, references: TSchema[]) { + const clone = CloneType(schema) + clone.properties = FromProperties(clone.properties, references) + return clone +} +// prettier-ignore +function FromPromise(schema: TPromise, references: TSchema[]) { + const clone = CloneType(schema) + clone.item = Deref(clone.item, references) + return clone +} +// prettier-ignore +function FromAsyncIterator(schema: TAsyncIterator, references: TSchema[]) { + const clone = CloneType(schema) + clone.items = Deref(clone.items, references) + return clone +} +// prettier-ignore +function FromIterator(schema: TIterator, references: TSchema[]) { + const clone = CloneType(schema) + clone.items = Deref(clone.items, references) + return clone +} +// 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 ( + IsConstructorType(schema) ? FromConstructor(schema, references) : + IsFunctionType(schema) ? FromFunction(schema, references) : + IsIntersectType(schema) ? FromIntersect(schema, references) : + IsUnionType(schema) ? FromUnion(schema, references) : + IsTupleType(schema) ? FromTuple(schema, references) : + IsArrayType(schema) ? FromArray(schema, references) : + IsObjectType(schema) ? FromObject(schema, references) : + IsPromiseType(schema) ? FromPromise(schema, references) : + IsAsyncIteratorType(schema) ? FromAsyncIterator(schema, references) : + IsIteratorType(schema) ? FromIterator(schema, references) : + IsRefType(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(schema, references) +} diff --git a/src/type/deref/index.ts b/src/type/deref/index.ts new file mode 100644 index 000000000..496b4d6f0 --- /dev/null +++ b/src/type/deref/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './deref' diff --git a/src/type/discard/discard.ts b/src/type/discard/discard.ts new file mode 100644 index 000000000..537455b2c --- /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 Key(value: Record, key: PropertyKey) { + const { [key]: _, ...rest } = value + return rest +} +export function Discard(value: Record, keys: PropertyKey[]) { + return keys.reduce((acc, key) => Key(acc, key), value) +} diff --git a/src/type/discard/index.ts b/src/type/discard/index.ts new file mode 100644 index 000000000..43fb9af56 --- /dev/null +++ b/src/type/discard/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './discard' diff --git a/examples/prototypes/const.ts b/src/type/distinct/distinct.ts similarity index 50% rename from examples/prototypes/const.ts rename to src/type/distinct/distinct.ts index 75399a0aa..079dcab9e 100644 --- a/examples/prototypes/const.ts +++ b/src/type/distinct/distinct.ts @@ -1,6 +1,6 @@ /*-------------------------------------------------------------------------- -@sinclair/typebox/prototypes +@sinclair/typebox/type The MIT License (MIT) @@ -26,36 +26,44 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { Type, ObjectMap, TypeGuard, TSchema, TIntersect, TUnion, TObject, TProperties, TReadonly, AssertProperties, AssertType, AssertRest, Evaluate, ReadonlyUnwrapType } from '@sinclair/typebox' +import { ExtendsCheck, ExtendsResult } from '../extends/index' +import { TSchema } from '../schema/index' -// ------------------------------------------------------------------------------------- -// TConst -// ------------------------------------------------------------------------------------- +// ------------------------------------------------------------------ +// Includes +// ------------------------------------------------------------------ // prettier-ignore -export type TConstArray = T extends [infer L, ...infer R] - ? [TConst, E>, ...TConstArray, E>] - : [] +type Includes = + T extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? C extends L + ? true + : Includes + : false // prettier-ignore -export type TConstProperties = Evaluate, E> -}>> +function Includes(T: TSchema[], C: TSchema): boolean { + const [L, ...R] = T + return T.length > 0 + ? ExtendsCheck(C, L) === ExtendsResult.True + ? true + : Includes(R, C) + : false +} +// ------------------------------------------------------------------ +// DistinctType +// ------------------------------------------------------------------ // 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 type DistinctType = + T extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? Includes extends false + ? DistinctType + : DistinctType + : Acc +// prettier-ignore +export function DistinctType(T: TSchema[], Acc: TSchema[] = []): TSchema[] { + const [L, ...R] = T + return T.length > 0 + ? Includes(Acc, L) === false + ? DistinctType(R, [...Acc, L]) + : DistinctType(R, [...Acc]) + : Acc +} diff --git a/src/type/distinct/index.ts b/src/type/distinct/index.ts new file mode 100644 index 000000000..2fcd7f1f3 --- /dev/null +++ b/src/type/distinct/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 './distinct' diff --git a/src/type/enum/enum.ts b/src/type/enum/enum.ts new file mode 100644 index 000000000..9740f9aaa --- /dev/null +++ b/src/type/enum/enum.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 { type TLiteral, Literal } from '../literal/index' +import { Kind, Hint } from '../symbols/index' +import { Union } from '../union/index' +import { IsUndefined } from '../guard/value' + +// ------------------------------------------------------------------ +// TEnum +// ------------------------------------------------------------------ +export type TEnumRecord = Record +export type TEnumValue = string | number +export type TEnumKey = string +export interface TEnum = Record> extends TSchema { + [Kind]: 'Union' + [Hint]: 'Enum' + static: T[keyof T] + anyOf: TLiteral[] +} +/** `[Json]` Creates a Enum type */ +export function Enum>(item: T, options: SchemaOptions = {}): TEnum { + if (IsUndefined(item)) throw new Error('Enum undefined or empty') + const values1 = globalThis.Object.getOwnPropertyNames(item) + .filter((key) => isNaN(key as any)) + .map((key) => item[key]) as T[keyof T][] + const values2 = [...new Set(values1)] + const anyOf = values2.map((value) => Literal(value)) + return Union(anyOf, { ...options, [Hint]: 'Enum' }) as unknown as TEnum +} diff --git a/src/type/enum/index.ts b/src/type/enum/index.ts new file mode 100644 index 000000000..d5dfd4a6f --- /dev/null +++ b/src/type/enum/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './enum' diff --git a/src/type/exclude/exclude.ts b/src/type/exclude/exclude.ts new file mode 100644 index 000000000..9dda8a71f --- /dev/null +++ b/src/type/exclude/exclude.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 { TSchema, SchemaOptions } from '../schema/index' +import type { UnionToTuple, AssertRest, AssertType, Assert } from '../helpers/index' +import { type TTemplateLiteral, TemplateLiteralToUnion } from '../template-literal/index' +import { type TUnion, Union } from '../union/index' +import { type TNever, Never } from '../never/index' +import { type TLiteral } from '../literal/index' +import { type Static } from '../static/index' +import { ExtendsCheck, ExtendsResult } from '../extends/index' +import { UnionResolve } from '../union/index' +import { CloneType } from '../clone/type' +import { TTemplateLiteral as IsTemplateLiteralType, TUnion as IsUnionType } from '../guard/type' + +// ------------------------------------------------------------------ +// ExcludeResolve +// ------------------------------------------------------------------ +// prettier-ignore +type ExcludeTemplateLiteralResult = UnionResolve }[T]>>> +// prettier-ignore +type ExcludeTemplateLiteral = ( + Exclude, Static> extends infer S ? ExcludeTemplateLiteralResult> : never +) +// prettier-ignore +type ExcludeArray = AssertRest> extends Static ? never : T[K] +}[number]>> extends infer R extends TSchema[] ? UnionResolve : never +// prettier-ignore +type ExcludeResolve = + T extends TTemplateLiteral ? ExcludeTemplateLiteral : + T extends TUnion ? ExcludeArray : + T extends U + ? TNever + : T +// prettier-ignore +export function ExcludeResolve(L: L, R: R): ExcludeResolve { + return ( + IsTemplateLiteralType(L) ? ExcludeResolve(TemplateLiteralToUnion(L), R) : + IsTemplateLiteralType(R) ? ExcludeResolve(L, TemplateLiteralToUnion(R)) : + IsUnionType(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 ExcludeResolve +} +// ------------------------------------------------------------------ +// TExclude +// ------------------------------------------------------------------ +export type TExclude = ExcludeResolve + +/** `[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 { + const E = ExcludeResolve(unionType, excludedMembers) as any + return CloneType(E, options) as TExclude +} diff --git a/src/type/exclude/index.ts b/src/type/exclude/index.ts new file mode 100644 index 000000000..4ed911329 --- /dev/null +++ b/src/type/exclude/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 './exclude' diff --git a/src/type/extends/extends-check.ts b/src/type/extends/extends-check.ts new file mode 100644 index 000000000..13fdfd736 --- /dev/null +++ b/src/type/extends/extends-check.ts @@ -0,0 +1,764 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF 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 } 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 { TypeGuard, ValueGuard } from '../guard/index' + +export class ExtendsResolverError extends Error {} + +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.TNever(right) || + TypeGuard.TIntersect(right) || + TypeGuard.TUnion(right) || + TypeGuard.TUnknown(right) || + TypeGuard.TAny(right) + ) +} +// prettier-ignore +function StructuralRight(left: TSchema, right: TSchema) { + 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 +// ------------------------------------------------------------------ +// prettier-ignore +function TAnyRight(left: TSchema, right: TAny) { + return ExtendsResult.True +} +// prettier-ignore +function TAny(left: TAny, right: TSchema) { + return ( + TypeGuard.TIntersect(right) ? TIntersectRight(left, right) : + (TypeGuard.TUnion(right) && right.anyOf.some((schema) => TypeGuard.TAny(schema) || TypeGuard.TUnknown(schema))) ? ExtendsResult.True : + TypeGuard.TUnion(right) ? ExtendsResult.Union : + TypeGuard.TUnknown(right) ? ExtendsResult.True : + TypeGuard.TAny(right) ? ExtendsResult.True : + ExtendsResult.Union + ) +} +// ------------------------------------------------------------------ +// Array +// ------------------------------------------------------------------ +// prettier-ignore +function TArrayRight(left: TSchema, right: TArray) { + return ( + TypeGuard.TUnknown(left) ? ExtendsResult.False : + TypeGuard.TAny(left) ? ExtendsResult.Union : + TypeGuard.TNever(left) ? ExtendsResult.True : + ExtendsResult.False + ) +} +// prettier-ignore +function TArray(left: TArray, right: TSchema) { + return ( + TypeGuard.TObject(right) && IsObjectArrayLike(right) ? ExtendsResult.True : + IsStructuralRight(right) ? StructuralRight(left, right) : + !TypeGuard.TArray(right) ? ExtendsResult.False : + IntoBooleanResult(Visit(left.items, right.items)) + ) +} +// ------------------------------------------------------------------ +// AsyncIterator +// ------------------------------------------------------------------ +// prettier-ignore +function TAsyncIterator(left: TAsyncIterator, right: TSchema) { + return ( + IsStructuralRight(right) ? StructuralRight(left, right) : + !TypeGuard.TAsyncIterator(right) ? ExtendsResult.False : + IntoBooleanResult(Visit(left.items, right.items)) + ) +} +// ------------------------------------------------------------------ +// BigInt +// ------------------------------------------------------------------ +// prettier-ignore +function TBigInt(left: TBigInt, right: TSchema): ExtendsResult { + return ( + IsStructuralRight(right) ? StructuralRight(left, right) : + TypeGuard.TObject(right) ? TObjectRight(left, right) : + TypeGuard.TRecord(right) ? TRecordRight(left, right) : + TypeGuard.TBigInt(right) ? ExtendsResult.True : + ExtendsResult.False + ) +} +// ------------------------------------------------------------------ +// Boolean +// ------------------------------------------------------------------ +// prettier-ignore +function TBooleanRight(left: TSchema, right: TBoolean) { + return ( + TypeGuard.TLiteralBoolean(left) ? ExtendsResult.True : + TypeGuard.TBoolean(left) ? ExtendsResult.True : + ExtendsResult.False + ) +} +// prettier-ignore +function TBoolean(left: TBoolean, right: TSchema): ExtendsResult { + return ( + IsStructuralRight(right) ? StructuralRight(left, right) : + TypeGuard.TObject(right) ? TObjectRight(left, right) : + TypeGuard.TRecord(right) ? TRecordRight(left, right) : + TypeGuard.TBoolean(right) ? ExtendsResult.True : + ExtendsResult.False + ) +} +// ------------------------------------------------------------------ +// Constructor +// ------------------------------------------------------------------ +// prettier-ignore +function TConstructor(left: TConstructor, right: TSchema) { + return ( + IsStructuralRight(right) ? StructuralRight(left, right) : + TypeGuard.TObject(right) ? TObjectRight(left, right) : + !TypeGuard.TConstructor(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 TDate(left: TDate, right: TSchema) { + return ( + IsStructuralRight(right) ? StructuralRight(left, right) : + TypeGuard.TObject(right) ? TObjectRight(left, right) : + TypeGuard.TRecord(right) ? TRecordRight(left, right) : + TypeGuard.TDate(right) ? ExtendsResult.True : + ExtendsResult.False + ) +} +// ------------------------------------------------------------------ +// Function +// ------------------------------------------------------------------ +// prettier-ignore +function TFunction(left: TFunction, right: TSchema) { + return ( + IsStructuralRight(right) ? StructuralRight(left, right) : + TypeGuard.TObject(right) ? TObjectRight(left, right) : + !TypeGuard.TFunction(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 TIntegerRight(left: TSchema, right: TInteger) { + return ( + TypeGuard.TLiteral(left) && ValueGuard.IsNumber(left.const) ? ExtendsResult.True : + TypeGuard.TNumber(left) || TypeGuard.TInteger(left) ? ExtendsResult.True : + ExtendsResult.False + ) +} +// prettier-ignore +function TInteger(left: TInteger, right: TSchema): ExtendsResult { + return ( + TypeGuard.TInteger(right) || TypeGuard.TNumber(right) ? ExtendsResult.True : + IsStructuralRight(right) ? StructuralRight(left, right) : + TypeGuard.TObject(right) ? TObjectRight(left, right) : + TypeGuard.TRecord(right) ? TRecordRight(left, right) : + ExtendsResult.False + ) +} +// ------------------------------------------------------------------ +// Intersect +// ------------------------------------------------------------------ +// prettier-ignore +function TIntersectRight(left: TSchema, right: TIntersect): ExtendsResult { + return right.allOf.every((schema) => Visit(left, schema) === ExtendsResult.True) + ? ExtendsResult.True + : ExtendsResult.False +} +// prettier-ignore +function TIntersect(left: TIntersect, right: TSchema) { + return left.allOf.some((schema) => Visit(schema, right) === ExtendsResult.True) + ? ExtendsResult.True + : ExtendsResult.False +} +// ------------------------------------------------------------------ +// Iterator +// ------------------------------------------------------------------ +// prettier-ignore +function TIterator(left: TIterator, right: TSchema) { + return ( + IsStructuralRight(right) ? StructuralRight(left, right) : + !TypeGuard.TIterator(right) ? ExtendsResult.False : + IntoBooleanResult(Visit(left.items, right.items)) + ) +} +// ------------------------------------------------------------------ +// Literal +// ------------------------------------------------------------------ +// prettier-ignore +function TLiteral(left: TLiteral, right: TSchema): ExtendsResult { + return ( + TypeGuard.TLiteral(right) && right.const === left.const ? ExtendsResult.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) : + ExtendsResult.False + ) +} +// ------------------------------------------------------------------ +// Never +// ------------------------------------------------------------------ +// prettier-ignore +function TNeverRight(left: TSchema, right: TNever) { + return ExtendsResult.False +} +// prettier-ignore +function TNever(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.TNot(current)) break + current = current.not + depth += 1 + } + return depth % 2 === 0 ? current : Unknown() +} +// prettier-ignore +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 +// ------------------------------------------------------------------ +// prettier-ignore +function TNull(left: TNull, right: TSchema) { + return ( + IsStructuralRight(right) ? StructuralRight(left, right) : + TypeGuard.TObject(right) ? TObjectRight(left, right) : + TypeGuard.TRecord(right) ? TRecordRight(left, right) : + TypeGuard.TNull(right) ? ExtendsResult.True : + ExtendsResult.False + ) +} +// ------------------------------------------------------------------ +// Number +// ------------------------------------------------------------------ +// prettier-ignore +function TNumberRight(left: TSchema, right: TNumber) { + return ( + TypeGuard.TLiteralNumber(left) ? ExtendsResult.True : + TypeGuard.TNumber(left) || TypeGuard.TInteger(left) ? ExtendsResult.True : + ExtendsResult.False + ) +} +// prettier-ignore +function TNumber(left: TNumber, right: TSchema): ExtendsResult { + return ( + IsStructuralRight(right) ? StructuralRight(left, right) : + TypeGuard.TObject(right) ? TObjectRight(left, right) : + TypeGuard.TRecord(right) ? TRecordRight(left, right) : + TypeGuard.TInteger(right) || TypeGuard.TNumber(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.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]) + )) + ) +} +// 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 = Function([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.TOptional(left) && !TypeGuard.TOptional(right) ? ExtendsResult.False : + ExtendsResult.True + ) +} +// prettier-ignore +function TObjectRight(left: TSchema, right: TObject) { + return ( + TypeGuard.TUnknown(left) ? ExtendsResult.False : + TypeGuard.TAny(left) ? ExtendsResult.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)) + ) ? ExtendsResult.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' ? ExtendsResult.True : ExtendsResult.False + })() : + (TypeGuard.TRecord(left) && TypeGuard.TNumber(RecordKey(left))) ? (() => { + return IsObjectPropertyCount(right, 0) ? ExtendsResult.True : ExtendsResult.False + })() : + ExtendsResult.False + ) +} +// prettier-ignore +function TObject(left: TObject, right: TSchema) { + return ( + IsStructuralRight(right) ? StructuralRight(left, right) : + TypeGuard.TRecord(right) ? TRecordRight(left, right) : + !TypeGuard.TObject(right) ? ExtendsResult.False : + (() => { + for (const key of Object.getOwnPropertyNames(right.properties)) { + if (!(key in left.properties) && !TypeGuard.TOptional(right.properties[key])) { + return ExtendsResult.False + } + if (TypeGuard.TOptional(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 TPromise(left: TPromise, right: TSchema) { + return ( + IsStructuralRight(right) ? StructuralRight(left, right) : + TypeGuard.TObject(right) && IsObjectPromiseLike(right) ? ExtendsResult.True : + !TypeGuard.TPromise(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 TRecordRight(left: TSchema, right: TRecord) { + const [Key, Value] = [RecordKey(right), RecordValue(right)] + return ( + ( + TypeGuard.TLiteralString(left) && TypeGuard.TNumber(Key) && IntoBooleanResult(Visit(left, Value)) === ExtendsResult.True) ? ExtendsResult.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]) === ExtendsResult.False) { + return ExtendsResult.False + } + } + return ExtendsResult.True + })() : + ExtendsResult.False + ) +} +// prettier-ignore +function TRecord(left: TRecord, right: TSchema) { + return ( + IsStructuralRight(right) ? StructuralRight(left, right) : + TypeGuard.TObject(right) ? TObjectRight(left, right) : + !TypeGuard.TRecord(right) ? ExtendsResult.False : + Visit(RecordValue(left), RecordValue(right)) + ) +} +// ------------------------------------------------------------------ +// String +// ------------------------------------------------------------------ +// prettier-ignore +function TStringRight(left: TSchema, right: TString) { + return ( + TypeGuard.TLiteral(left) && ValueGuard.IsString(left.const) ? ExtendsResult.True : + TypeGuard.TString(left) ? ExtendsResult.True : + ExtendsResult.False + ) +} +// prettier-ignore +function TString(left: TString, right: TSchema): ExtendsResult { + return ( + IsStructuralRight(right) ? StructuralRight(left, right) : + TypeGuard.TObject(right) ? TObjectRight(left, right) : + TypeGuard.TRecord(right) ? TRecordRight(left, right) : + TypeGuard.TString(right) ? ExtendsResult.True : + ExtendsResult.False + ) +} +// ------------------------------------------------------------------ +// Symbol +// ------------------------------------------------------------------ +// prettier-ignore +function TSymbol(left: TSymbol, right: TSchema): ExtendsResult { + return ( + IsStructuralRight(right) ? StructuralRight(left, right) : + TypeGuard.TObject(right) ? TObjectRight(left, right) : + TypeGuard.TRecord(right) ? TRecordRight(left, right) : + TypeGuard.TSymbol(right) ? ExtendsResult.True : + ExtendsResult.False + ) +} +// ------------------------------------------------------------------ +// TemplateLiteral +// ------------------------------------------------------------------ +// prettier-ignore +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. + return ( + TypeGuard.TTemplateLiteral(left) ? Visit(TemplateLiteralToUnion(left), right) : + TypeGuard.TTemplateLiteral(right) ? Visit(left, TemplateLiteralToUnion(right)) : + Throw('Invalid fallthrough for TemplateLiteral') + ) +} +// ------------------------------------------------------------------ +// Tuple +// ------------------------------------------------------------------ +// prettier-ignore +function IsArrayOfTuple(left: TTuple, right: TSchema) { + return ( + TypeGuard.TArray(right) && + left.items !== undefined && + left.items.every((schema) => Visit(schema, right.items) === ExtendsResult.True) + ) +} +// prettier-ignore +function TTupleRight(left: TSchema, right: TTuple) { + return ( + TypeGuard.TNever(left) ? ExtendsResult.True : + TypeGuard.TUnknown(left) ? ExtendsResult.False : + TypeGuard.TAny(left) ? ExtendsResult.Union : + ExtendsResult.False + ) +} +// prettier-ignore +function TTuple(left: TTuple, right: TSchema): ExtendsResult { + return ( + IsStructuralRight(right) ? StructuralRight(left, right) : + TypeGuard.TObject(right) && IsObjectArrayLike(right) ? ExtendsResult.True : + TypeGuard.TArray(right) && IsArrayOfTuple(left, right) ? ExtendsResult.True : + !TypeGuard.TTuple(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 TUint8Array(left: TUint8Array, right: TSchema) { + return ( + IsStructuralRight(right) ? StructuralRight(left, right) : + TypeGuard.TObject(right) ? TObjectRight(left, right) : + TypeGuard.TRecord(right) ? TRecordRight(left, right) : + TypeGuard.TUint8Array(right) ? ExtendsResult.True : + ExtendsResult.False + ) +} +// ------------------------------------------------------------------ +// Undefined +// ------------------------------------------------------------------ +// prettier-ignore +function TUndefined(left: TUndefined, right: TSchema) { + 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) ? ExtendsResult.True : + ExtendsResult.False + ) +} +// ------------------------------------------------------------------ +// Union +// ------------------------------------------------------------------ +// prettier-ignore +function TUnionRight(left: TSchema, right: TUnion): ExtendsResult { + return right.anyOf.some((schema) => Visit(left, schema) === ExtendsResult.True) + ? ExtendsResult.True + : ExtendsResult.False +} +// prettier-ignore +function TUnion(left: TUnion, right: TSchema): ExtendsResult { + return left.anyOf.every((schema) => Visit(schema, right) === ExtendsResult.True) + ? ExtendsResult.True + : ExtendsResult.False +} +// ------------------------------------------------------------------ +// Unknown +// ------------------------------------------------------------------ +// prettier-ignore +function TUnknownRight(left: TSchema, right: TUnknown) { + return ExtendsResult.True +} +// prettier-ignore +function TUnknown(left: TUnknown, right: TSchema) { + 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) ? ExtendsResult.True : + ExtendsResult.False + ) +} +// ------------------------------------------------------------------ +// Void +// ------------------------------------------------------------------ +// prettier-ignore +function VoidRight(left: TSchema, right: TVoid) { + return ( + TypeGuard.TUndefined(left) ? ExtendsResult.True : + TypeGuard.TUndefined(left) ? ExtendsResult.True : + ExtendsResult.False + ) +} +// prettier-ignore +function TVoid(left: TVoid, right: TSchema) { + 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) ? ExtendsResult.True : + ExtendsResult.False + ) +} +// prettier-ignore +function Visit(left: TSchema, right: TSchema): ExtendsResult { + 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 ExtendsCheck(left: TSchema, right: TSchema): ExtendsResult { + return Visit(left, right) +} diff --git a/src/type/extends/extends-undefined.ts b/src/type/extends/extends-undefined.ts new file mode 100644 index 000000000..f6c275c6a --- /dev/null +++ b/src/type/extends/extends-undefined.ts @@ -0,0 +1,55 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema } from '../schema/index' +import type { TIntersect } from '../intersect/index' +import type { TUnion } from '../union/index' +import type { TNot } from '../not/index' +import { Kind } from '../symbols/index' + +/** Fast undefined check used for properties of type undefined */ +function Intersect(schema: TIntersect) { + return schema.allOf.every((schema) => ExtendsUndefinedCheck(schema)) +} +function Union(schema: TUnion) { + return schema.anyOf.some((schema) => ExtendsUndefinedCheck(schema)) +} +function Not(schema: TNot) { + return !ExtendsUndefinedCheck(schema.not) +} +/** Fast undefined check used for properties of type undefined */ +// prettier-ignore +export function ExtendsUndefinedCheck(schema: TSchema): boolean { + return ( + schema[Kind] === 'Intersect' ? Intersect(schema as TIntersect) : + schema[Kind] === 'Union' ? Union(schema as TUnion) : + schema[Kind] === 'Not' ? Not(schema as TNot) : + schema[Kind] === 'Undefined' ? true : + false + ) +} diff --git a/src/type/extends/extends.ts b/src/type/extends/extends.ts new file mode 100644 index 000000000..86e11c0ee --- /dev/null +++ b/src/type/extends/extends.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 type { Static } from '../static/index' +import { type TUnion, Union } from '../union/index' +import { ExtendsCheck, ExtendsResult } from './extends-check' +import { UnionToTuple } from '../helpers/index' +import { CloneType } from '../clone/type' + +// prettier-ignore +export type ExtendsResolve = ( + (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 +export function ExtendsResolve(left: L, right: R, trueType: T, falseType: U): ExtendsResolve { + const R = ExtendsCheck(left, right) + return ( + R === ExtendsResult.Union ? Union([trueType, falseType]) : + R === ExtendsResult.True ? trueType : + falseType + ) as unknown as ExtendsResolve +} + +// ------------------------------------------------------------------ +// TExtends +// ------------------------------------------------------------------ +export type TExtends = ExtendsResolve + +/** `[Json]` Creates a Conditional type */ +export function Extends(L: L, R: R, T: T, F: F, options: SchemaOptions = {}): TExtends { + const E = ExtendsResolve(L, R, T, F) + return CloneType(E, options) as TExtends +} diff --git a/src/type/extends/index.ts b/src/type/extends/index.ts new file mode 100644 index 000000000..e1d7e7e76 --- /dev/null +++ b/src/type/extends/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 './extends-check' +export * from './extends-undefined' +export * from './extends' diff --git a/src/type/extract/extract.ts b/src/type/extract/extract.ts new file mode 100644 index 000000000..19d873dfd --- /dev/null +++ b/src/type/extract/extract.ts @@ -0,0 +1,84 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF 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 TTemplateLiteral, TemplateLiteralToUnion } from '../template-literal/index' +import { type TLiteral } from '../literal/index' +import { type TUnion, Union } from '../union/index' +import { type Static } from '../static/index' +import { Never } from '../never/index' +import { UnionResolve } from '../union/index' +import { ExtendsCheck, ExtendsResult } from '../extends/index' +import { CloneType } from '../clone/type' +import { TTemplateLiteral as IsTemplateLiteralType, TUnion as IsUnionType } from '../guard/type' + +// ------------------------------------------------------------------ +// ExtractResolve +// ------------------------------------------------------------------ +// prettier-ignore +type FromTemplateLiteralResult = UnionResolve }[T]>>> +// prettier-ignore +type FromTemplateLiteral = Extract, Static> extends infer S ? FromTemplateLiteralResult> : never +// prettier-ignore +type FromArray = AssertRest> extends Static ? T[K] : never +}[number]>> extends infer R extends TSchema[] ? UnionResolve : never +// prettier-ignore +export type ExtractResolve = ( + T extends TTemplateLiteral ? FromTemplateLiteral : + T extends TUnion ? FromArray : + T extends U + ? T + : T // ? +) +// prettier-ignore +export function ExtractResolve(L: L, R: R): ExtractResolve { + return ( + IsTemplateLiteralType(L) ? ExtractResolve(TemplateLiteralToUnion(L), R) : + IsTemplateLiteralType(R) ? ExtractResolve(L, TemplateLiteralToUnion(R) as any) : + IsUnionType(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 ExtractResolve +} + +// ------------------------------------------------------------------ +// TExtract +// ------------------------------------------------------------------ +// prettier-ignore +export type TExtract = ExtractResolve + +/** `[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 { + const E = ExtractResolve(type, union) + return CloneType(E, options) as TExtract +} diff --git a/src/type/extract/index.ts b/src/type/extract/index.ts new file mode 100644 index 000000000..7335c0f12 --- /dev/null +++ b/src/type/extract/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 './extract' diff --git a/src/type/function/function.ts b/src/type/function/function.ts new file mode 100644 index 000000000..d03855159 --- /dev/null +++ b/src/type/function/function.ts @@ -0,0 +1,68 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF 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' + +// ------------------------------------------------------------------ +// FunctionResolve +// ------------------------------------------------------------------ +type FunctionReturnTypeResolve = Static + +// prettier-ignore +type FunctionParametersResolve = + T extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? [Static, ...FunctionParametersResolve] + : [] +// prettier-ignore +type FunctionResolve = ( + Ensure<(...param: FunctionParametersResolve) => FunctionReturnTypeResolve> +) +// ------------------------------------------------------------------ +// TFunction +// ------------------------------------------------------------------ +export interface TFunction extends TSchema { + [Kind]: 'Function' + static: FunctionResolve + type: 'Function' + parameters: T + returns: U +} +/** `[JavaScript]` Creates a Function type */ +export function Function(parameters: [...T], returns: U, options?: SchemaOptions): TFunction { + return { + ...options, + [Kind]: 'Function', + type: 'Function', + parameters: CloneRest(parameters), + returns: CloneType(returns), + } as unknown as TFunction +} diff --git a/src/type/function/index.ts b/src/type/function/index.ts new file mode 100644 index 000000000..3399c7270 --- /dev/null +++ b/src/type/function/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './function' diff --git a/src/type/guard/index.ts b/src/type/guard/index.ts new file mode 100644 index 000000000..2e7cc05ad --- /dev/null +++ b/src/type/guard/index.ts @@ -0,0 +1,30 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * as TypeGuard from './type' +export * as ValueGuard from './value' diff --git a/src/type/guard/type.ts b/src/type/guard/type.ts new file mode 100644 index 000000000..388d66230 --- /dev/null +++ b/src/type/guard/type.ts @@ -0,0 +1,576 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF 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 type { TransformOptions } from '../transform/index' +import type { TTemplateLiteral, TTemplateLiteralKind } from '../template-literal/index' +import type { TArray } from '../array/index' +import type { TBoolean } from '../boolean/index' +import type { TRecord } from '../record/index' +import type { TString } from '../string/index' +import type { TUnion } from '../union/index' +import type { TAny } from '../any/index' +import type { TAsyncIterator } from '../async-iterator/index' +import type { TBigInt } from '../bigint/index' +import type { TConstructor } from '../constructor/index' +import type { TFunction } from '../function/index' +import type { TInteger } from '../integer/index' +import type { TIntersect } from '../intersect/index' +import type { TIterator } from '../iterator/index' +import type { TLiteral } from '../literal/index' +import type { 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 } 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 { 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 Error {} + +const KnownTypes = [ + 'Any', + 'Array', + 'AsyncIterator', + 'BigInt', + 'Boolean', + 'Constructor', + 'Date', + 'Enum', + 'Function', + 'Integer', + 'Intersect', + 'Iterator', + 'Literal', + 'Not', + 'Null', + 'Number', + 'Object', + 'Promise', + 'Record', + 'Ref', + '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) || 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) +} +// ------------------------------------------------------------------ +// Modifiers +// ------------------------------------------------------------------ +/** Returns true if this value has a Readonly symbol */ +export function TReadonly(schema: T): schema is TReadonly { + return ValueGuard.IsObject(schema) && schema[ReadonlyKind] === 'Readonly' +} +/** Returns true if this value has a Optional symbol */ +export function TOptional(schema: T): schema is TOptional { + return ValueGuard.IsObject(schema) && schema[OptionalKind] === 'Optional' +} +// ------------------------------------------------------------------ +// 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 ValueGuard.IsObject(schema) && Kind in schema && schema[Kind] === 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) && + Object.entries(schema.properties).every(([key, schema]) => IsControlCharacterFree(key) && TSchema(schema)) && + IsAdditionalProperties(schema.additionalProperties) && + IsOptionalNumber(schema.minProperties) && + IsOptionalNumber(schema.maxProperties) + ) +} +/** 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 { [TransformKind]: TransformOptions } { + return ValueGuard.IsObject(schema) && TransformKind 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 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]) && !KnownTypes.includes(schema[Kind] as string) +} +/** 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) + ) +} diff --git a/src/type/guard/value.ts b/src/type/guard/value.ts new file mode 100644 index 000000000..f6b88f10d --- /dev/null +++ b/src/type/guard/value.ts @@ -0,0 +1,84 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION 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 string */ +export function IsString(value: unknown): value is string { + return typeof value === 'string' +} +/** Returns true if this value is symbol */ +export function IsSymbol(value: unknown): value is symbol { + return typeof value === 'symbol' +} +/** Returns true if this value is a Uint8Array */ +export function IsUint8Array(value: unknown): value is Uint8Array { + return value instanceof globalThis.Uint8Array +} +/** Returns true if this value is undefined */ +export function IsUndefined(value: unknown): value is undefined { + return value === undefined +} diff --git a/src/type/helpers/helpers.ts b/src/type/helpers/helpers.ts new file mode 100644 index 000000000..d47ec65d2 --- /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] ? [] : [...UnionToTuple>, L] +export type Trim = T extends `${' '}${infer U}` ? Trim : T extends `${infer U}${' '}` ? Trim : T +export type Assert = T extends E ? T : never +export type Evaluate = T extends infer O ? { [K in keyof O]: O[K] } : never +export type Ensure = T extends infer U ? U : never +export type EmptyString = '' +export type ZeroString = '0' +// ------------------------------------------------------------------ +// Helper: Increment +// ------------------------------------------------------------------ +type IncrementBase = { m: '9'; t: '01'; '0': '1'; '1': '2'; '2': '3'; '3': '4'; '4': '5'; '5': '6'; '6': '7'; '7': '8'; '8': '9'; '9': '0' } +type IncrementTake = IncrementBase[T] +type IncrementStep = T extends IncrementBase['m'] + ? IncrementBase['t'] + : T extends `${infer L extends keyof IncrementBase}${infer R}` + ? L extends IncrementBase['m'] + ? `${IncrementTake}${IncrementStep}` + : `${IncrementTake}${R}` + : never +type IncrementReverse = T extends `${infer L}${infer R}` ? `${IncrementReverse}${L}` : T +export type Increment = IncrementReverse>> +/** Increments the given string value + 1 */ +export function Increment(T: T): Increment { + return (parseInt(T) + 1).toString() as Increment +} +// ------------------------------------------------------------------ +// Helper: Type Asserts +// ------------------------------------------------------------------ +export type AssertProperties = T extends TProperties ? T : TProperties +export type AssertRest = T extends E ? T : [] +export type AssertType = T extends E ? T : TNever diff --git a/src/type/helpers/index.ts b/src/type/helpers/index.ts new file mode 100644 index 000000000..4d6559c8a --- /dev/null +++ b/src/type/helpers/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './helpers' diff --git a/src/type/index.ts b/src/type/index.ts new file mode 100644 index 000000000..0d77f8003 --- /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 './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 './modifiers/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 './operators/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 './static/index' +export * from './strict/index' +export * from './string/index' +export * from './symbol/index' +export * from './symbols/index' +export * from './template-literal/index' +export * from './transform/index' +export * from './tuple/index' +export * from './type/index' +export * from './uint8array/index' +export * from './undefined/index' +export * from './union/index' +export * from './unknown/index' +export * from './unsafe/index' +export * from './void/index' diff --git a/src/type/indexed/index.ts b/src/type/indexed/index.ts new file mode 100644 index 000000000..ca6b25b2c --- /dev/null +++ b/src/type/indexed/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 './indexed-key' +export * from './indexed-type' +export * from './indexed' diff --git a/src/type/indexed/indexed-key.ts b/src/type/indexed/indexed-key.ts new file mode 100644 index 000000000..17f470f63 --- /dev/null +++ b/src/type/indexed/indexed-key.ts @@ -0,0 +1,115 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF 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, TemplateLiteralGenerate, TemplateLiteralParseExact, IsTemplateLiteralFinite, TemplateLiteralToUnion } from '../template-literal/index' +import { type TLiteral, type 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' +import { TTemplateLiteral as IsTemplateLiteralType, TUnionLiteral as IsUnionLiteralType, TUnion as IsUnionType, TLiteral as IsLiteralType, TNumber as IsNumberType, TInteger as IsIntegerType } from '../guard/type' + +// ------------------------------------------------------------------ +// FromTemplateLiteral +// ------------------------------------------------------------------ +// prettier-ignore +type FromTemplateLiteral> = ( + F extends true + ? TemplateLiteralGenerate extends infer R extends string[] ? R : [] + : [] +) +// prettier-ignore +function FromTemplateLiteral(T: T): FromTemplateLiteral { + const E = TemplateLiteralParseExact(T.pattern) + const F = IsTemplateLiteralFinite(E) + const S = TemplateLiteralToUnion(T) + return ( + F === true + ? IsUnionLiteralType(S) + ? S.anyOf.map(S => S.const.toString()) + : [] + : [] + ) as unknown as FromTemplateLiteral +} +// ------------------------------------------------------------------ +// FromUnion +// ------------------------------------------------------------------ +// prettier-ignore +type FromUnion = ( + T extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? [...IndexedKeyResolve, ...FromUnion] + : [] +) +// prettier-ignore +function FromUnion(T: T): FromUnion{ + const [L, ...R] = T + return ( + T.length > 0 + ? [...IndexedKeyResolve(L), ...FromUnion(R)] + : [] + ) as FromUnion +} +// ------------------------------------------------------------------ +// FromLiteral +// ------------------------------------------------------------------ +// prettier-ignore +type FromLiteral = ( + T extends PropertyKey + ? [`${T}`] + : [] +) +// prettier-ignore +function FromLiteral(T: T): FromLiteral { + return ( + [T.toString()] + ) as FromLiteral +} +// ------------------------------------------------------------------ +// IndexedKeyResolve +// ------------------------------------------------------------------ +// prettier-ignore +export type IndexedKeyResolve = ( + T extends TTemplateLiteral ? FromTemplateLiteral : + T extends TUnion ? FromUnion : + T extends TLiteral ? FromLiteral : + T extends TNumber ? ['[number]'] : + T extends TInteger ? ['[number]'] : + [] +) +/** Resolves Indexer String Keys form the given Schema. Th */ +// prettier-ignore +export function IndexedKeyResolve(T: T): IndexedKeyResolve { + return [...new Set(( + IsTemplateLiteralType(T) ? FromTemplateLiteral(T) : + IsUnionType(T) ? FromUnion(T.anyOf) : + IsLiteralType(T) ? FromLiteral(T.const) : + IsNumberType(T) ? ['[number]'] : + IsIntegerType(T) ? ['[number]'] : + [] + ))] as unknown as IndexedKeyResolve +} diff --git a/src/type/indexed/indexed-type.ts b/src/type/indexed/indexed-type.ts new file mode 100644 index 000000000..4754613ba --- /dev/null +++ b/src/type/indexed/indexed-type.ts @@ -0,0 +1,228 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF 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 TObject, type TProperties } from '../object/index' +import { type TNever, Never } from '../never/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 { IntersectResolve } from '../intersect/index' +import { UnionResolve } from '../union/index' + +import { TIntersect as IsIntersectType, TUnion as IsUnionType, TTuple as IsTupleType, TArray as IsArrayType, TObject as IsObjectType, TNever as IsNeverType } from '../guard/type' +// ------------------------------------------------------------------ +// FromRest +// ------------------------------------------------------------------ +// prettier-ignore +type FromRest = ( + T extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? [FromKey, ...FromRest] + : [] +) +// prettier-ignore +function FromRest(T: [...T], K: K): FromRest { + const [L, ...R] = T + return ( + T.length > 0 + ? [FromKey(L, K), ...FromRest(R, K)] + : [] + ) as FromRest +} +// ------------------------------------------------------------------ +// FromIntersectRest +// ------------------------------------------------------------------ +// prettier-ignore +type FromIntersectRest = ( + T extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? L extends TNever + ? [...FromIntersectRest] + : [L, ...FromIntersectRest] + : [] +) +// prettier-ignore +function FromIntersectRest(T: [...T]): FromIntersectRest { + const [L, ...R] = T + return ( + T.length > 0 + ? IsNeverType(L) + ? [...FromIntersectRest(R)] + : [L, ...FromIntersectRest(R)] + : [] + ) as FromIntersectRest +} +// prettier-ignore +type FromIntersect = ( + IntersectResolve>> +) +// prettier-ignore +function FromIntersect(T: [...T], K: K): FromIntersect { + return ( + IntersectResolve(FromIntersectRest(FromRest(T as TSchema[], K))) + ) as FromIntersect +} +// ------------------------------------------------------------------ +// FromUnionRest +// ------------------------------------------------------------------ +// prettier-ignore +type FromUnionRest = ( + T extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? L extends TNever + ? [] + : FromUnionRest + : S +) +// prettier-ignore +function FromUnionRest(T: [...T], S = T): FromUnionRest { + const [L, ...R] = T + return ( + T.length > 0 + ? IsNeverType(L) + ? [] + : FromUnionRest(R, S) + : S + ) as FromUnionRest +} +// prettier-ignore +type FromUnion = ( + UnionResolve>> +) +// prettier-ignore +function FromUnion(T: [...T], K: K): FromUnion { + return ( + UnionResolve(FromUnionRest(FromRest(T as TSchema[], K))) + ) as FromUnion +} +// ------------------------------------------------------------------ +// FromTuple +// ------------------------------------------------------------------ +// prettier-ignore +type FromTuple = ( + K extends keyof T ? T[K] : + K extends '[number]' ? UnionResolve : + TNever +) +// prettier-ignore +function FromTuple(T: [...T], K: K): FromTuple { + return ( + K in T ? T[K as number] : + K === '[number]' ? UnionResolve(T) : + Never() + ) as FromTuple +} +// ------------------------------------------------------------------ +// FromArray +// ------------------------------------------------------------------ +// prettier-ignore +type FromArray = ( + K extends '[number]' + ? T + : TNever +) +// prettier-ignore +function FromArray(T: T, K: K): FromArray { + return ( + K === '[number]' + ? T + : Never() + ) as FromArray +} +// ------------------------------------------------------------------ +// FromProperty +// ------------------------------------------------------------------ +// prettier-ignore +type FromProperty = ( + K extends keyof T + ? T[K] + : TNever +) +// prettier-ignore +function FromProperty(T: T, K: K): FromProperty { + return ( + K in T + ? T[K as string] + : Never() + ) as FromProperty +} +// ------------------------------------------------------------------ +// FromKey +// ------------------------------------------------------------------ +// prettier-ignore +type FromKey = ( + T extends TRecursive ? FromKey : + T extends TIntersect ? FromIntersect : + T extends TUnion ? FromUnion : + T extends TTuple ? FromTuple : + T extends TArray ? FromArray : + T extends TObject ? FromProperty : + TNever +) +// prettier-ignore +function FromKey(T: T, K: K): FromKey { + return ( + IsIntersectType(T) ? FromIntersect(T.allOf, K) : + IsUnionType(T) ? FromUnion(T.anyOf, K) : + IsTupleType(T) ? FromTuple(T.items ?? [], K) : + IsArrayType(T) ? FromArray(T.items, K) : + IsObjectType(T) ? FromProperty(T.properties, K) : + Never() + ) as FromKey +} +// ------------------------------------------------------------------ +// FromKeys +// ------------------------------------------------------------------ +// prettier-ignore +type FromKeys = ( + K extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]] + ? [FromKey, ...FromKeys] + : [] +) +// prettier-ignore +function FromKeys(T: T, K: [...K]): FromKeys { + const [L, ...R] = K + return ( + K.length > 0 + ? [FromKey(T, L), ...FromKeys(T, R)] + : [] + ) as FromKeys +} +// ------------------------------------------------------------------ +// IndexedTypeResolve +// ------------------------------------------------------------------ +// prettier-ignore +export type IndexedTypeResolve = ( + UnionResolve> +) +// prettier-ignore +export function IndexedTypeResolve(T: T, K: [...K]): IndexedTypeResolve { + return ( + UnionResolve(FromKeys(T, K as PropertyKey[])) + ) as IndexedTypeResolve +} diff --git a/src/type/indexed/indexed.ts b/src/type/indexed/indexed.ts new file mode 100644 index 000000000..1ecf5bbcc --- /dev/null +++ b/src/type/indexed/indexed.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 { IndexedTypeResolve } from './indexed-type' +import { IndexedKeyResolve } from './indexed-key' +import { CloneType } from '../clone/type' +import { TSchema as IsSchemaType } from '../guard/type' +// ------------------------------------------------------------------ +// TIndex +// ------------------------------------------------------------------ +export type TIndex = IndexedTypeResolve + +/** `[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 { + const keys = IsSchemaType(K) ? IndexedKeyResolve(K) : (K as string[]) + const type = IndexedTypeResolve(T, keys) + return CloneType(type, options) +} diff --git a/src/type/instance-type/index.ts b/src/type/instance-type/index.ts new file mode 100644 index 000000000..aa85b717a --- /dev/null +++ b/src/type/instance-type/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './instance-type' diff --git a/src/type/instance-type/instance-type.ts b/src/type/instance-type/instance-type.ts new file mode 100644 index 000000000..ffc7093f5 --- /dev/null +++ b/src/type/instance-type/instance-type.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 type { TSchema, SchemaOptions } from '../schema/index' +import type { TConstructor } from '../constructor/index' +import { CloneType } from '../clone/type' + +// ------------------------------------------------------------------ +// TInstanceType +// ------------------------------------------------------------------ +export type TInstanceType> = T['returns'] + +/** `[JavaScript]` Extracts the InstanceType from the given Constructor type */ +export function InstanceType>(schema: T, options: SchemaOptions = {}): TInstanceType { + return CloneType(schema.returns, options) +} diff --git a/src/type/integer/index.ts b/src/type/integer/index.ts new file mode 100644 index 000000000..73513ebc8 --- /dev/null +++ b/src/type/integer/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './integer' diff --git a/src/type/integer/integer.ts b/src/type/integer/integer.ts new file mode 100644 index 000000000..513d94c7c --- /dev/null +++ b/src/type/integer/integer.ts @@ -0,0 +1,54 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF 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' + +// ------------------------------------------------------------------ +// TInteger +// ------------------------------------------------------------------ +export interface IntegerOptions extends SchemaOptions { + exclusiveMaximum?: number + exclusiveMinimum?: number + maximum?: number + minimum?: number + multipleOf?: number +} +export interface TInteger extends TSchema, IntegerOptions { + [Kind]: 'Integer' + static: number + type: 'integer' +} +/** `[Json]` Creates an Integer type */ +export function Integer(options: IntegerOptions = {}): TInteger { + return { + ...options, + [Kind]: 'Integer', + type: 'integer', + } as unknown as TInteger +} diff --git a/src/type/intersect/index.ts b/src/type/intersect/index.ts new file mode 100644 index 000000000..59135edfc --- /dev/null +++ b/src/type/intersect/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 './intersect' diff --git a/src/type/intersect/intersect.ts b/src/type/intersect/intersect.ts new file mode 100644 index 000000000..4fc3a4693 --- /dev/null +++ b/src/type/intersect/intersect.ts @@ -0,0 +1,79 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF 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 { TupleToIntersect, AssertType } from '../helpers/index' +import type { TSchema, SchemaOptions } from '../schema/index' +import type { Static } from '../static/index' +import { type TNever, Never } from '../never/index' +import { OptionalFromIntersect } from '../modifiers/index' +import { Kind } from '../symbols/index' +import { CloneType, CloneRest } from '../clone/type' +import { TTransform as IsTransformType, TObject as IsObjectType, TSchema as IsSchemaType } from '../guard/type' + +// ------------------------------------------------------------------ +// IntersectResolve +// ------------------------------------------------------------------ +// prettier-ignore +export type IntersectResolve = ( + T extends [] ? TNever : + T extends [TSchema] ? T[0] : + OptionalFromIntersect +) +// prettier-ignore +export function IntersectResolve(T: T): IntersectResolve { + return ( + T.length === 0 + ? Never() + : T.length === 1 + ? T[0] + : OptionalFromIntersect(T)) as IntersectResolve +} +// ------------------------------------------------------------------ +// 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] +} +/** `[Json]` Creates an Intersect type */ +export function Intersect(T: [...T], options: IntersectOptions = {}): IntersectResolve { + if (T.length === 0) return Never() as IntersectResolve + if (T.length === 1) return CloneType(T[0], options) as IntersectResolve + if (T.some((schema) => IsTransformType(schema))) throw new Error('Cannot intersect transform types') + const allObjects = T.every((schema) => IsObjectType(schema)) + const clonedUnevaluatedProperties = IsSchemaType(options.unevaluatedProperties) ? { unevaluatedProperties: CloneType(options.unevaluatedProperties) } : {} + return (options.unevaluatedProperties === false || IsSchemaType(options.unevaluatedProperties) || allObjects + ? { ...options, ...clonedUnevaluatedProperties, [Kind]: 'Intersect', type: 'object', allOf: CloneRest(T) } + : { ...options, ...clonedUnevaluatedProperties, [Kind]: 'Intersect', allOf: CloneRest(T) }) as unknown as IntersectResolve +} diff --git a/src/type/intrinsic/index.ts b/src/type/intrinsic/index.ts new file mode 100644 index 000000000..6c20265fb --- /dev/null +++ b/src/type/intrinsic/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 './intrinsic' diff --git a/src/type/intrinsic/intrinsic.ts b/src/type/intrinsic/intrinsic.ts new file mode 100644 index 000000000..48daf0810 --- /dev/null +++ b/src/type/intrinsic/intrinsic.ts @@ -0,0 +1,171 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF 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 TTemplateLiteral, type TTemplateLiteralKind, TemplateLiteral, TemplateLiteralParseExact, IsTemplateLiteralFinite, TemplateLiteralGenerate } from '../template-literal/index' +import { type TLiteral, type TLiteralValue, Literal } from '../literal/index' +import { type TUnion, Union } from '../union/index' +import { TTemplateLiteral as IsTemplateLiteralType, TUnion as IsUnionType, TLiteral as IsLiteralType } from '../guard/type' +import { CloneType } from '../clone/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 +// ------------------------------------------------------------------ +type IntrinsicMode = 'Uppercase' | 'Lowercase' | 'Capitalize' | 'Uncapitalize' +// ------------------------------------------------------------------ +// FromTemplateLiteral +// ------------------------------------------------------------------ +// prettier-ignore +type FromTemplateLiteral = + M extends IntrinsicMode ? + T extends [infer L extends TTemplateLiteralKind, ...infer R extends TTemplateLiteralKind[]] + ? [IntrinsicResolve, ...FromTemplateLiteral] + : T + : T +function FromTemplateLiteral(schema: TTemplateLiteral, mode: IntrinsicMode): FromTemplateLiteral { + // 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 = IsTemplateLiteralFinite(expression) + if (!finite) return { ...schema, pattern: FromLiteralValue(schema.pattern, mode) } as any + const strings = [...TemplateLiteralGenerate(expression)] + const literals = strings.map((value) => Literal(value)) + const mapped = FromRest(literals as any, mode) + const union = Union(mapped) + return TemplateLiteral([union]) as unknown as FromTemplateLiteral +} +// ------------------------------------------------------------------ +// FromLiteralValue +// ------------------------------------------------------------------ +// prettier-ignore +type FromLiteralValue = ( + 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 FromRest = + T extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? [IntrinsicResolve, ...FromRest] + : [] +function FromRest(T: [...T], mode: M): FromRest { + const [L, ...R] = T + return (T.length > 0 ? [IntrinsicResolve(L, mode), ...FromRest(R, mode)] : []) as FromRest +} +// ------------------------------------------------------------------ +// IntrinsicResolve +// ------------------------------------------------------------------ +// prettier-ignore +export type IntrinsicResolve = + T extends TTemplateLiteral ? TTemplateLiteral> : + T extends TUnion ? TUnion> : + T extends TLiteral ? TLiteral> : + T +/** Applies an intrinsic string manipulation to the given type. */ +// prettier-ignore +export function IntrinsicResolve(schema: T, mode: M): IntrinsicResolve { + return ( + IsTemplateLiteralType(schema) ? FromTemplateLiteral(schema, mode) : + IsUnionType(schema) ? Union(FromRest(schema.anyOf, mode)) : + IsLiteralType(schema) ? Literal(FromLiteralValue(schema.const, mode)) : + schema + ) as IntrinsicResolve +} + +// ------------------------------------------------------------------ +// TUncapitalize +// ------------------------------------------------------------------ +// prettier-ignore +export type TUncapitalize = IntrinsicResolve +/** `[Json]` Intrinsic function to Uncapitalize LiteralString types */ +export function Uncapitalize(T: T, options: SchemaOptions = {}): TUncapitalize { + return CloneType(IntrinsicResolve(T, 'Uncapitalize'), options) +} +// ------------------------------------------------------------------ +// TUppercase +// ------------------------------------------------------------------ +// prettier-ignore +export type TUppercase = IntrinsicResolve +/** `[Json]` Intrinsic function to Uppercase LiteralString types */ +export function Uppercase(T: T, options: SchemaOptions = {}): TUppercase { + return CloneType(IntrinsicResolve(T, 'Uppercase'), options) +} +// ------------------------------------------------------------------ +// TLowercase +// ------------------------------------------------------------------ +// prettier-ignore +export type TLowercase = IntrinsicResolve +/** `[Json]` Intrinsic function to Lowercase LiteralString types */ +export function Lowercase(T: T, options: SchemaOptions = {}): TLowercase { + return CloneType(IntrinsicResolve(T, 'Lowercase'), options) +} +// ------------------------------------------------------------------ +// TCapitalize +// ------------------------------------------------------------------ +// prettier-ignore +export type TCapitalize = IntrinsicResolve +/** `[Json]` Intrinsic function to Capitalize LiteralString types */ +export function Capitalize(T: T, options: SchemaOptions = {}): TCapitalize { + return CloneType(IntrinsicResolve(T, 'Capitalize'), options) +} diff --git a/src/type/iterator/index.ts b/src/type/iterator/index.ts new file mode 100644 index 000000000..8c41f54e3 --- /dev/null +++ b/src/type/iterator/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './iterator' diff --git a/src/type/iterator/iterator.ts b/src/type/iterator/iterator.ts new file mode 100644 index 000000000..66b75bfbc --- /dev/null +++ b/src/type/iterator/iterator.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 type { Static } from '../static/index' +import { Kind } from '../symbols/index' +import { CloneType } from '../clone/type' + +// ------------------------------------------------------------------ +// TIterator +// ------------------------------------------------------------------ +export interface TIterator extends TSchema { + [Kind]: 'Iterator' + static: IterableIterator> + type: 'Iterator' + items: T +} +/** `[JavaScript]` Creates an Iterator type */ +export function Iterator(items: T, options: SchemaOptions = {}): TIterator { + return { + ...options, + [Kind]: 'Iterator', + type: 'Iterator', + items: CloneType(items), + } as unknown as TIterator +} diff --git a/src/type/keyof/index.ts b/src/type/keyof/index.ts new file mode 100644 index 000000000..61708fc40 --- /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-string' +export * from './keyof-type' +export * from './keyof' diff --git a/src/type/keyof/keyof-string.ts b/src/type/keyof/keyof-string.ts new file mode 100644 index 000000000..dd38ed96e --- /dev/null +++ b/src/type/keyof/keyof-string.ts @@ -0,0 +1,178 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF 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 { OperatorUnionMany, OperatorIntersectMany } from '../operators/index' +import { TIntersect as IsIntersectType, TUnion as IsUnionType, TTuple as IsTupleType, TArray as IsArrayType, TObject as IsObjectType, TRecord as IsRecordType } from '../guard/type' + +// ------------------------------------------------------------------ +// FromRest +// ------------------------------------------------------------------ +// prettier-ignore +type FromRest = ( + T extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? [KeyOfStringResolve, ...FromRest] + : [] +) +// prettier-ignore +function FromRest(T: [...T]): FromRest { + const [L, ...R] = T + return ( + T.length > 0 + ? [KeyOfStringResolve(L), ...FromRest(R)] + : [] + ) as FromRest +} +// ------------------------------------------------------------------ +// FromIntersect +// ------------------------------------------------------------------ +// prettier-ignore +type FromIntersect> = ( + OperatorUnionMany +) +// prettier-ignore +function FromIntersect(T: [...T], C = FromRest(T)): FromIntersect { + return ( + OperatorUnionMany(C as PropertyKey[][]) as FromIntersect + ) +} +// ------------------------------------------------------------------ +// FromUnion +// ------------------------------------------------------------------ +// prettier-ignore +type FromUnion> = ( + OperatorIntersectMany +) +// prettier-ignore +function FromUnion(T: [...T]): FromUnion { + const C = FromRest(T) as PropertyKey[][] + return ( + OperatorIntersectMany(C) as FromUnion + ) +} +// ------------------------------------------------------------------ +// FromTuple +// ------------------------------------------------------------------ +// prettier-ignore +type FromTuple = + T extends [infer _ extends TSchema, ...infer R extends TSchema[]] + ? [I, ...FromTuple>] + : [] +// prettier-ignore +function FromTuple(T: [...T], I = '0'): FromTuple { + const [_, ...R] = T + return ( + T.length > 0 + ? [I, ...FromTuple(R, Increment(I))] + : [] + ) as FromTuple +} +// ------------------------------------------------------------------ +// FromArray +// ------------------------------------------------------------------ +// prettier-ignore +type FromArray<_ extends TSchema> = ( + ['[number]'] +) +// prettier-ignore +function FromArray<_ extends TSchema>(_: _): FromArray<_> { + return ( + ['[number]'] + ) +} +// ------------------------------------------------------------------ +// FromProperties +// ------------------------------------------------------------------ +// prettier-ignore +type FromProperties = ( + UnionToTuple +) +// prettier-ignore +function FromProperties(T: T): FromProperties { + return ( + globalThis.Object.getOwnPropertyNames(T) + ) as FromProperties +} +// ------------------------------------------------------------------ +// 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 + }) +} +// ------------------------------------------------------------------ +// KeyOfStringResolve +// ------------------------------------------------------------------ +// prettier-ignore +export type KeyOfStringResolve = ( + T extends TRecursive ? KeyOfStringResolve : + T extends TIntersect ? FromIntersect : + T extends TUnion ? FromUnion : + T extends TTuple ? FromTuple : + T extends TArray ? FromArray : + T extends TObject ? FromProperties : + [] +) +/** Resolves finite keys from this type */ +// prettier-ignore +export function KeyOfStringResolve(T: T): KeyOfStringResolve { + return ( + IsIntersectType(T) ? FromIntersect(T.allOf) : + IsUnionType(T) ? FromUnion(T.anyOf) : + IsTupleType(T) ? FromTuple(T.items ?? []) : + IsArrayType(T) ? FromArray(T.items) : + IsObjectType(T) ? FromProperties(T.properties) : + IsRecordType(T) ? FromPatternProperties(T.patternProperties) : + [] + ) as KeyOfStringResolve +} +// ---------------------------------------------------------------- +// KeyOfStringResolvePattern +// ---------------------------------------------------------------- +let includePatternProperties = false +/** Resolves keys as a regular expression, including pattern property keys */ +export function KeyOfStringResolvePattern(schema: TSchema): string { + includePatternProperties = true + const keys = KeyOfStringResolve(schema) + includePatternProperties = false + const pattern = keys.map((key) => `(${key})`) + return `^(${pattern.join('|')})$` +} diff --git a/src/type/keyof/keyof-type.ts b/src/type/keyof/keyof-type.ts new file mode 100644 index 000000000..88ccb5b42 --- /dev/null +++ b/src/type/keyof/keyof-type.ts @@ -0,0 +1,70 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF 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 { type TLiteral, TLiteralValue, Literal } from '../literal/index' +import { type TNumber, Number } from '../number/index' +import { KeyOfStringResolve } from './keyof-string' +import { UnionResolve } from '../union/index' + +// ------------------------------------------------------------------ +// FromLiterals +// ------------------------------------------------------------------ +// prettier-ignore +type FromLiterals = ( + T extends [infer L extends TLiteralValue, ...infer R extends TLiteralValue[]] + ? L extends '[number]' + ? [TNumber, ...FromLiterals] + : [TLiteral, ...FromLiterals] + : [] +) +// prettier-ignore +function FromLiterals(T: [...T]): FromLiterals { + const [L, ...R] = T + return ( + T.length > 0 + ? L === '[number]' + ? [Number(), ...FromLiterals(R)] + : [Literal(L as TLiteralValue), ...FromLiterals(R)] + : [] + ) as FromLiterals +} +// ------------------------------------------------------------------ +// KeyOfTypeResolve +// ------------------------------------------------------------------ +// prettier-ignore +export type KeyOfTypeResolve = ( + Ensure>>> +) +// prettier-ignore +export function KeyOfTypeResolve(T: T): KeyOfTypeResolve { + return ( + UnionResolve(FromLiterals(KeyOfStringResolve(T) as TLiteralValue[])) + ) as unknown as KeyOfTypeResolve +} diff --git a/src/type/keyof/keyof.ts b/src/type/keyof/keyof.ts new file mode 100644 index 000000000..2c0a4b35d --- /dev/null +++ b/src/type/keyof/keyof.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 { KeyOfTypeResolve } from './keyof-type' +import { CloneType } from '../clone/type' + +// ------------------------------------------------------------------ +// TKeyOf +// ------------------------------------------------------------------ +export type TKeyOf = KeyOfTypeResolve + +/** `[Json]` Creates a KeyOf type */ +export function KeyOf(schema: T, options: SchemaOptions = {}): KeyOfTypeResolve { + const K = KeyOfTypeResolve(schema) + return CloneType(K, options) as KeyOfTypeResolve +} diff --git a/src/type/literal/index.ts b/src/type/literal/index.ts new file mode 100644 index 000000000..d4cb53ef2 --- /dev/null +++ b/src/type/literal/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './literal' diff --git a/src/type/literal/literal.ts b/src/type/literal/literal.ts new file mode 100644 index 000000000..8754ffd7c --- /dev/null +++ b/src/type/literal/literal.ts @@ -0,0 +1,53 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema, SchemaOptions } from '../schema/index' +import { Kind } from '../symbols/index' + +// ------------------------------------------------------------------ +// TLiteralValue +// ------------------------------------------------------------------ +export type TLiteralValue = boolean | number | string // | bigint - supported but variant disable due to potential numeric type conflicts + +// ------------------------------------------------------------------ +// TLiteral +// ------------------------------------------------------------------ +export interface TLiteral extends TSchema { + [Kind]: 'Literal' + static: T + const: T +} +/** `[Json]` Creates a Literal type */ +export function Literal(value: T, options: SchemaOptions = {}): TLiteral { + return { + ...options, + [Kind]: 'Literal', + const: value, + type: typeof value as 'string' | 'number' | 'boolean', + } as unknown as TLiteral +} diff --git a/src/type/mapped/index.ts b/src/type/mapped/index.ts new file mode 100644 index 000000000..67272965f --- /dev/null +++ b/src/type/mapped/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 './mapped' diff --git a/src/type/mapped/mapped.ts b/src/type/mapped/mapped.ts new file mode 100644 index 000000000..d05857a59 --- /dev/null +++ b/src/type/mapped/mapped.ts @@ -0,0 +1,116 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF 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 { Static } from '../static/index' +import { Object, type TObject, type TProperties } from '../object/index' +import { Literal } from '../literal/index' +import { IndexedKeyResolve } from '../indexed/indexed-key' +import { TSchema as IsSchemaType } from '../guard/type' +import { Kind } from '../symbols/index' + +// ------------------------------------------------------------------ +// TMappedKey +// ------------------------------------------------------------------ +// prettier-ignore +export interface TMappedKey extends TSchema { + [Kind]: 'MappedKey' + static: T[number] + anyOf: [] +} +// ------------------------------------------------------------------ +// MappedFunction +// ------------------------------------------------------------------ +// prettier-ignore +export type MappedFunction> = (T: I) => TSchema +// ------------------------------------------------------------------ +// MappedResult +// ------------------------------------------------------------------ +// prettier-ignore +export type MappedResultProperties = + K extends [infer L extends string, ...infer R extends PropertyKey[]] + ? {[_ in `${L}`]: T} & MappedResultProperties + : {} + +// ------------------------------------------------------------------ +// EvaluateMappedReturnType +// ------------------------------------------------------------------ +export type UnevaluatedMappedResult = T + +// TSchema should be any schema type, but may contain an UnevaluatedMappedResult. Any +// internal UnevaluatedMappedResult's must be remapped using the current key K to index +// into the result. The structure of UnevaluatedMappedResult is yet to be defined, however +// it should be possible to write overloads for both Extends and Index such that they +// return the UnevaluatedMappedResult when returned in the Mapped type. This +export type EvaluatedMappedResult = T + +// prettier-ignore +export interface TMappedResult< + K extends PropertyKey[], + F extends MappedFunction, + S extends TSchema = ReturnType, + R extends TSchema = EvaluatedMappedResult, + P extends TProperties = Evaluate>, + O extends TObject = TObject

+> extends TSchema { + [Kind]: 'MappedResult', + static: Static, + properties: P +} +// ------------------------------------------------------------------ +// Mapped +// ------------------------------------------------------------------ +// prettier-ignore +/** `[Experimental]` Creates a Mapped type */ +export function Mapped< + K extends TSchema, + I extends PropertyKey[] = IndexedKeyResolve, + F extends MappedFunction = MappedFunction, + R extends TMappedResult = TMappedResult +>(key: K, map: F): R + +// prettier-ignore +/** `[Experimental]` Creates a Mapped type */ +export function Mapped< + K extends PropertyKey[], + F extends MappedFunction = MappedFunction, + R extends TMappedResult = TMappedResult +>(key: [...K], map: F): R + +// prettier-ignore +/** `[Experimental]` Creates a Mapped type */ +export function Mapped(key: any, map: Function) { + const keys = IsSchemaType(key) ? IndexedKeyResolve(key) : key as PropertyKey[] + const anyOf = keys.map(key => Literal(key as string)) + const mapped = { [Kind]: 'MappedKey', anyOf } + const properties = keys.reduce((acc, key) => { + return { ...acc, [key]: map(mapped) } + }, {} as TProperties) + return Object(properties) +} diff --git a/src/type/modifiers/index.ts b/src/type/modifiers/index.ts new file mode 100644 index 000000000..e57239326 --- /dev/null +++ b/src/type/modifiers/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 './modifiers' diff --git a/src/type/modifiers/modifiers.ts b/src/type/modifiers/modifiers.ts new file mode 100644 index 000000000..75387d047 --- /dev/null +++ b/src/type/modifiers/modifiers.ts @@ -0,0 +1,149 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF 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 } from '../readonly/index' +import { type TIntersect, Intersect } from '../intersect/index' +import { type TUnion, Union } from '../union/index' +import { type TOptional, Optional } from '../optional/index' +import { Discard } from '../discard/index' +import { OptionalKind } from '../symbols/index' +import { TOptional as IsOptionalType } from '../guard/type' + +// ------------------------------------------------------------------ +// IsOptionalFromIntersect +// ------------------------------------------------------------------ +// prettier-ignore +type IsOptionalFromIntersect = ( + T extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? L extends TOptional + ? IsOptionalFromIntersect + : false + : true +) +// prettier-ignore +function IsOptionalFromIntersect(T: T): IsOptionalFromIntersect { + const [L, ...R] = T + return ( + T.length > 0 + ? IsOptionalType(L) + ? IsOptionalFromIntersect(R) + : false + : true + ) as IsOptionalFromIntersect +} +// ------------------------------------------------------------------ +// IsOptionalFromUnion +// ------------------------------------------------------------------ +// prettier-ignore +type IsOptionalFromUnion = ( + T extends [infer L extends TSchema, ...infer R extends TSchema[]] ? + L extends TOptional + ? true + : IsOptionalFromUnion + : false +) +// prettier-ignore +function IsOptionalFromUnion(T: T): IsOptionalFromUnion { + const [L, ...R] = T + return ( + T.length > 0 + ? IsOptionalType(L) + ? true + : IsOptionalFromUnion(R) + : false + ) as IsOptionalFromUnion +} +// ------------------------------------------------------------------ +// RemoveOptionalFromRest +// ------------------------------------------------------------------ +// prettier-ignore +type RemoveOptionalFromRest = ( + T extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? L extends TOptional + ? [RemoveOptionalFromSchema, ...RemoveOptionalFromRest] + : [L, ...RemoveOptionalFromRest] + : [] +) +// prettier-ignore +function RemoveOptionalFromRest(T: T): RemoveOptionalFromRest { + return ( + T.map(T => RemoveOptionalFromSchema(T)) + ) as RemoveOptionalFromRest +} +// ------------------------------------------------------------------ +// RemoveOptionalFromSchema +// ------------------------------------------------------------------ +// prettier-ignore +type RemoveOptionalFromSchema = ( + T extends TReadonly ? TReadonly> : + T extends TOptional ? RemoveOptionalFromSchema : + T +) +// prettier-ignore +function RemoveOptionalFromSchema(T: T): RemoveOptionalFromSchema { + return ( + Discard(T, [OptionalKind]) + ) as RemoveOptionalFromSchema +} +// ------------------------------------------------------------------ +// OptionalFromIntersect +// ------------------------------------------------------------------ +// prettier-ignore +export type OptionalFromIntersect> = ( + IsOptionalFromIntersect extends true + ? TOptional> + : TIntersect +) +// prettier-ignore +export function OptionalFromIntersect(T: T): OptionalFromIntersect { + const D = RemoveOptionalFromRest(T) as TSchema[] + return ( + IsOptionalFromIntersect(T) + ? Optional(Intersect(D)) + : Intersect(T) + ) as unknown as OptionalFromIntersect +} +// ------------------------------------------------------------------ +// OptionalFromUnion +// ------------------------------------------------------------------ +// prettier-ignore +export type OptionalFromUnion> = ( + IsOptionalFromUnion extends true + ? TOptional> + : TUnion +) +// prettier-ignore +export function OptionalFromUnion(T: T): OptionalFromUnion { + const D = RemoveOptionalFromRest(T) as TSchema[] + return ( + IsOptionalFromUnion(T) + ? Optional(Union(D)) + : Union(T) + ) as OptionalFromUnion +} diff --git a/src/type/never/index.ts b/src/type/never/index.ts new file mode 100644 index 000000000..913504405 --- /dev/null +++ b/src/type/never/index.ts @@ -0,0 +1,28 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ +export * from './never' diff --git a/src/type/never/never.ts b/src/type/never/never.ts new file mode 100644 index 000000000..89c73c925 --- /dev/null +++ b/src/type/never/never.ts @@ -0,0 +1,47 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF 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' + +// ------------------------------------------------------------------ +// TNever +// ------------------------------------------------------------------ +export interface TNever extends TSchema { + [Kind]: 'Never' + static: never + not: {} +} +/** `[Json]` Creates a Never type */ +export function Never(options: SchemaOptions = {}): TNever { + return { + ...options, + [Kind]: 'Never', + not: {}, + } as unknown as TNever +} diff --git a/src/type/not/index.ts b/src/type/not/index.ts new file mode 100644 index 000000000..49cf9dca8 --- /dev/null +++ b/src/type/not/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './not' diff --git a/src/type/not/not.ts b/src/type/not/not.ts new file mode 100644 index 000000000..71c1f3c44 --- /dev/null +++ b/src/type/not/not.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 { Static } from '../static/index' +import { Kind } from '../symbols/index' +import { CloneType } from '../clone/type' + +// ------------------------------------------------------------------ +// TNot +// ------------------------------------------------------------------ +export interface TNot extends TSchema { + [Kind]: 'Not' + static: T extends TNot ? Static : unknown + not: T +} +/** `[Json]` Creates a Not type */ +export function Not(schema: T, options?: SchemaOptions): TNot { + return { + ...options, + [Kind]: 'Not', + not: CloneType(schema), + } as unknown as TNot +} diff --git a/src/type/null/index.ts b/src/type/null/index.ts new file mode 100644 index 000000000..281bef4f4 --- /dev/null +++ b/src/type/null/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './null' diff --git a/src/type/null/null.ts b/src/type/null/null.ts new file mode 100644 index 000000000..6c0f5cf38 --- /dev/null +++ b/src/type/null/null.ts @@ -0,0 +1,47 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF 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' + +// ------------------------------------------------------------------ +// TNull +// ------------------------------------------------------------------ +export interface TNull extends TSchema { + [Kind]: 'Null' + static: null + type: 'null' +} +/** `[Json]` Creates a Null type */ +export function Null(options: SchemaOptions = {}): TNull { + return { + ...options, + [Kind]: 'Null', + type: 'null', + } as unknown as TNull +} diff --git a/src/type/number/index.ts b/src/type/number/index.ts new file mode 100644 index 000000000..c43f623c7 --- /dev/null +++ b/src/type/number/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './number' diff --git a/src/type/number/number.ts b/src/type/number/number.ts new file mode 100644 index 000000000..d047ea8ef --- /dev/null +++ b/src/type/number/number.ts @@ -0,0 +1,54 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF 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' + +// ------------------------------------------------------------------ +// TNumber +// ------------------------------------------------------------------ +export interface NumberOptions extends SchemaOptions { + exclusiveMaximum?: number + exclusiveMinimum?: number + maximum?: number + minimum?: number + multipleOf?: number +} +export interface TNumber extends TSchema, NumberOptions { + [Kind]: 'Number' + static: number + type: 'number' +} +/** `[Json]` Creates a Number type */ +export function Number(options: NumberOptions = {}): TNumber { + return { + ...options, + [Kind]: 'Number', + type: 'number', + } as unknown as TNumber +} diff --git a/src/type/object/index.ts b/src/type/object/index.ts new file mode 100644 index 000000000..5bebd5824 --- /dev/null +++ b/src/type/object/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './object' diff --git a/src/type/object/object.ts b/src/type/object/object.ts new file mode 100644 index 000000000..c47a039d2 --- /dev/null +++ b/src/type/object/object.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 { 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' +import { TOptional as IsOptionalType, TSchema as IsSchemaType } from '../guard/type' + +// ------------------------------------------------------------------ +// TProperties +// ------------------------------------------------------------------ +export type TPropertyKey = string | number +export type TProperties = Record +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 PropertiesResolve> = Evaluate<( + Readonly>>> & + Readonly>> & + Partial>> & + Required>> +)> +// ------------------------------------------------------------------ +// ObjectResolve +// ------------------------------------------------------------------ +// prettier-ignore +export type ObjectResolve = PropertiesResolve +}> +// ------------------------------------------------------------------ +// 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: ObjectResolve + 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) => IsOptionalType(properties[key])) + const requiredKeys = propertyKeys.filter((name) => !optionalKeys.includes(name)) + const clonedAdditionalProperties = IsSchemaType(options.additionalProperties) ? { additionalProperties: CloneType(options.additionalProperties) } : {} + const clonedProperties = propertyKeys.reduce((acc, key) => ({ ...acc, [key]: CloneType(properties[key]) }), {} as TProperties) + return (requiredKeys.length > 0 + ? { ...options, ...clonedAdditionalProperties, [Kind]: 'Object', type: 'object', properties: clonedProperties, required: requiredKeys } + : { ...options, ...clonedAdditionalProperties, [Kind]: 'Object', type: 'object', properties: clonedProperties }) as unknown as TObject +} + +/** `[Json]` Creates an Object type */ +export const Object = _Object diff --git a/src/type/omit/index.ts b/src/type/omit/index.ts new file mode 100644 index 000000000..d1fc93833 --- /dev/null +++ b/src/type/omit/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 './omit' diff --git a/src/type/omit/omit.ts b/src/type/omit/omit.ts new file mode 100644 index 000000000..2995c5176 --- /dev/null +++ b/src/type/omit/omit.ts @@ -0,0 +1,116 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF 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 { IndexedKeyResolve } from '../indexed/index' +import { Discard } from '../discard/index' +import { TransformKind } from '../symbols/index' +import { CloneType } from '../clone/type' +import { TIntersect as IsIntersectType, TUnion as IsUnionType, TObject as IsObjectType, TSchema as IsSchemaType } from '../guard/type' + +// ------------------------------------------------------------------ +// FromIntersect +// ------------------------------------------------------------------ +// prettier-ignore +type FromIntersect = + T extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? [OmitResolve, ...FromIntersect] + : [] +// prettier-ignore +function FromIntersect(T: T, K: K) { + return T.map((T) => OmitResolve(T, K)) as FromIntersect +} +// ------------------------------------------------------------------ +// FromUnion +// ------------------------------------------------------------------ +// prettier-ignore +type FromUnion = + T extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? [OmitResolve, ...FromUnion] + : [] +// prettier-ignore +function FromUnion(T: T, K: K) { + return T.map((T) => OmitResolve(T, K)) as FromUnion +} +// ------------------------------------------------------------------ +// FromProperty +// ------------------------------------------------------------------ +// prettier-ignore +function FromProperty, K extends PropertyKey>(T: T, K: K) { + const { [K]: _, ...R } = T + return R as TProperties +} +// prettier-ignore +type FromProperties> = 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 OmitResolve = + 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]): OmitResolve { + return ( + IsIntersectType(T) ? Intersect(FromIntersect(T.allOf, K)) : + IsUnionType(T) ? Union(FromUnion(T.anyOf, K)) : + IsObjectType(T) ? Object(FromProperties(T.properties, K)) : + Object({}) + ) as OmitResolve +} +// ------------------------------------------------------------------ +// TOmit +// ------------------------------------------------------------------ +// prettier-ignore +export type TOmit = OmitResolve + +/** `[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: TSchema | readonly PropertyKey[], options: SchemaOptions = {}): any { + const I = IsSchemaType(K) ? IndexedKeyResolve(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/operators/index.ts b/src/type/operators/index.ts new file mode 100644 index 000000000..59a6c6925 --- /dev/null +++ b/src/type/operators/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 './operators' diff --git a/src/type/operators/operators.ts b/src/type/operators/operators.ts new file mode 100644 index 000000000..927afb9a5 --- /dev/null +++ b/src/type/operators/operators.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. + +---------------------------------------------------------------------------*/ + +// ------------------------------------------------------------------ +// +// Operators (Set-Theory) +// +// The following functions perform common set-theory operations +// across arrays of PropertyKey. These operators are used in +// Intersect and Union composition. +// +// ------------------------------------------------------------------ + +// ------------------------------------------------------------------ +// OperatorIncludes +// ------------------------------------------------------------------ +// prettier-ignore +export type OperatorIncludes = ( + T extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]] + ? S extends L + ? true + : OperatorIncludes + : false +) +/** Returns true if element S is in the set of T */ +// prettier-ignore +export function OperatorIncludes(T: [...T], S: S): OperatorIncludes { + const [L, ...R] = T + return ( + T.length > 0 + ? S === L + ? true + : OperatorIncludes(R, S) + : false + ) as OperatorIncludes +} +// ------------------------------------------------------------------ +// OperatorSubset +// ------------------------------------------------------------------ +// prettier-ignore +export type OperatorSubset = + T extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]] + ? OperatorIncludes extends true + ? OperatorSubset + : false + : true + +/** Returns true if T is a subset of S */ +// prettier-ignore +export function OperatorSubset(T: [...T], S: [...S]): OperatorSubset { + const [L, ...R] = T + return ( + T.length > 0 + ? OperatorIncludes(S, L) === true + ? OperatorSubset(R, S) + : false + : true + ) as OperatorSubset +} +// ------------------------------------------------------------------ +// OperatorDistinct +// ------------------------------------------------------------------ +// prettier-ignore +export type OperatorDistinct = + T extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]] + ? OperatorIncludes extends false + ? OperatorDistinct + : OperatorDistinct + : Acc +/** Returns a distinct set of elements */ +// prettier-ignore +export function OperatorDistinct(T: [...T], Acc: PropertyKey[] = []): OperatorDistinct { + const [L, ...R] = T + return ( + T.length > 0 + ? OperatorIncludes(Acc, L) === false + ? OperatorDistinct(R, [...Acc, L]) + : OperatorDistinct(R, [...Acc]) + : Acc + ) as OperatorDistinct +} +// ------------------------------------------------------------------ +// OperatorIntersect +// ------------------------------------------------------------------ +// prettier-ignore +export type OperatorIntersect = ( + T extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]] + ? OperatorIncludes extends true + ? [L, ...OperatorIntersect] + : [...OperatorIntersect] + : [] +) +/** Returns the Intersect of the given sets */ +// prettier-ignore +export function OperatorIntersect(T: [...T], S: [...S]): OperatorIntersect { + const [L, ...R] = T + return ( + T.length > 0 + ? OperatorIncludes(S, L) === true + ? [L, ...OperatorIntersect(R, S)] + : [...OperatorIntersect(R, S)] + : [] + ) as OperatorIntersect +} +// ------------------------------------------------------------------ +// OperatorUnion +// ------------------------------------------------------------------ +// prettier-ignore +export type OperatorUnion = ( + T extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]] + ? OperatorIncludes extends true + ? [...OperatorUnion] + : [L, ...OperatorUnion] + : S +) +/** Returns the Union of the given sets */ +// prettier-ignore +export function OperatorUnion(T: [...T], S: [...S]): OperatorUnion { + const [L, ...R] = T + return ( + T.length > 0 + ? OperatorIncludes(S, L) === true + ? [...OperatorUnion(R, S)] + : [L, ...OperatorUnion(R, S)] + : S + ) as OperatorUnion +} +// ------------------------------------------------------------------ +// OperatorComplement +// ------------------------------------------------------------------ +// prettier-ignore +export type OperatorComplement = ( + T extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]] + ? OperatorIncludes extends true + ? [...OperatorComplement] + : [L, ...OperatorComplement] + : [] +) +/** Returns the Complement by omitting elements in T that are in S */ +// prettier-ignore +export function OperatorComplement(T: [...T], S: [...S]): OperatorComplement { + const [L, ...R] = T + return ( + T.length > 0 + ? OperatorIncludes(S, L) === true + ? [...OperatorComplement(R, S)] + : [L, ...OperatorComplement(R, S)] + : [] + ) as OperatorComplement +} +// ------------------------------------------------------------------ +// OperatorIntersectMany +// ------------------------------------------------------------------ +// prettier-ignore +export type OperatorIntersectMany = ( + T extends [infer L extends PropertyKey[]] + ? L + : T extends [infer L extends PropertyKey[], ...infer R extends PropertyKey[][]] + ? OperatorIntersect> + : [] +) +/** Returns the Intersect of multiple sets */ +// prettier-ignore +export function OperatorIntersectMany(T: [...T]): OperatorIntersectMany { + return ( + T.length === 1 + ? T[0] + : (() => { + const [L, ...R] = T + return ( + L.length > 0 + ? OperatorIntersect(L, OperatorIntersectMany(R)) + : [] + ) + })() + ) as OperatorIntersectMany +} +// ------------------------------------------------------------------ +// OperatorUnionMany +// ------------------------------------------------------------------ +// prettier-ignore +export type OperatorUnionMany = ( + T extends [infer L extends PropertyKey[]] + ? L + : T extends [infer L extends PropertyKey[], ...infer R extends PropertyKey[][]] + ? OperatorUnion> + : [] +) +/** Returns the Union of multiple sets */ +export function OperatorUnionMany(T: [...T]): OperatorUnionMany { + return ( + T.length === 1 + ? T[0] + : (() => { + const [L, ...R] = T + return T.length > 0 ? OperatorUnion(L, OperatorUnionMany(R)) : [] + })() + ) as OperatorUnionMany +} diff --git a/src/type/optional/index.ts b/src/type/optional/index.ts new file mode 100644 index 000000000..637cf0620 --- /dev/null +++ b/src/type/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 './optional' diff --git a/src/type/optional/optional.ts b/src/type/optional/optional.ts new file mode 100644 index 000000000..f55c77a2b --- /dev/null +++ b/src/type/optional/optional.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 type { TSchema } from '../schema/index' +import { OptionalKind } from '../symbols/index' +import { CloneType } from '../clone/type' + +// ------------------------------------------------------------------ +// TOptional +// ------------------------------------------------------------------ +export type TOptional = T & { [OptionalKind]: 'Optional' } + +/** `[Json]` Creates an Optional property */ +export function Optional(schema: T): TOptional { + return { ...CloneType(schema), [OptionalKind]: 'Optional' } +} diff --git a/src/type/parameters/index.ts b/src/type/parameters/index.ts new file mode 100644 index 000000000..195fafbb0 --- /dev/null +++ b/src/type/parameters/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './parameters' diff --git a/src/type/parameters/parameters.ts b/src/type/parameters/parameters.ts new file mode 100644 index 000000000..ab6ac76b0 --- /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 { type TTuple, Tuple } from '../tuple/index' +import { CloneRest } from '../clone/type' + +// ------------------------------------------------------------------ +// TParameters +// ------------------------------------------------------------------ +export type TParameters = Ensure> + +/** `[JavaScript]` Extracts the Parameters from the given Function type */ +export function Parameters>(schema: T, options: SchemaOptions = {}): TParameters { + return Tuple(CloneRest(schema.parameters), { ...options }) +} diff --git a/src/type/partial/index.ts b/src/type/partial/index.ts new file mode 100644 index 000000000..ce37bcee5 --- /dev/null +++ b/src/type/partial/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 './partial' diff --git a/src/type/partial/partial.ts b/src/type/partial/partial.ts new file mode 100644 index 000000000..0483310b9 --- /dev/null +++ b/src/type/partial/partial.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 { TSchema, SchemaOptions } from '../schema/index' +import type { Evaluate } from '../helpers/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 { TIntersect as IsIntersectType, TUnion as IsUnionType, TObject as IsObjectType } from '../guard/type' + +// ------------------------------------------------------------------ +// FromIntersect +// ------------------------------------------------------------------ +// prettier-ignore +type FromIntersect = ( + T extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? [PartialResolve, ...FromIntersect] + : [] +) +// prettier-ignore +function FromIntersect(T: [...T]) : FromIntersect { + const [L, ...R] = T + return ( + T.length > 0 + ? [PartialResolve(L), ...FromIntersect(R)] + : [] + ) as FromIntersect +} +// ------------------------------------------------------------------ +// FromUnion +// ------------------------------------------------------------------ +// prettier-ignore +type FromUnion = ( + T extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? [PartialResolve, ...FromUnion] + : [] +) +// prettier-ignore +function FromUnion(T: [...T]): FromUnion { + const [L, ...R] = T + return ( + T.length > 0 + ? [PartialResolve(L), ...FromUnion(R)] + : [] + ) as FromUnion +} +// ------------------------------------------------------------------ +// FromProperties +// ------------------------------------------------------------------ +// prettier-ignore +type FromProperties = 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 +export type PartialResolve = ( + T extends TRecursive ? TRecursive> : + T extends TIntersect ? TIntersect> : + T extends TUnion ? TUnion> : + T extends TObject ? TObject> : + TObject<{}> +) +// prettier-ignore +export function PartialResolve(T: T): PartialResolve { + return ( + IsIntersectType(T) ? Intersect(FromIntersect(T.allOf)) : + IsUnionType(T) ? Union(FromUnion(T.anyOf)) : + IsObjectType(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 = {}): TPartial { + const D = Discard(T, [TransformKind, '$id', 'required']) as TSchema + const R = CloneType(PartialResolve(T), options) + return { ...D, ...R } +} diff --git a/src/type/patterns/index.ts b/src/type/patterns/index.ts new file mode 100644 index 000000000..7dbf0a210 --- /dev/null +++ b/src/type/patterns/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './patterns' diff --git a/src/type/patterns/patterns.ts b/src/type/patterns/patterns.ts new file mode 100644 index 000000000..d9b9e8d79 --- /dev/null +++ b/src/type/patterns/patterns.ts @@ -0,0 +1,34 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export const PatternBoolean = '(true|false)' +export const PatternNumber = '(0|[1-9][0-9]*)' +export const PatternString = '(.*)' +export const PatternBooleanExact = `^${PatternBoolean}$` +export const PatternNumberExact = `^${PatternNumber}$` +export const PatternStringExact = `^${PatternString}$` diff --git a/src/type/pick/index.ts b/src/type/pick/index.ts new file mode 100644 index 000000000..9af323051 --- /dev/null +++ b/src/type/pick/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 './pick' diff --git a/src/type/pick/pick.ts b/src/type/pick/pick.ts new file mode 100644 index 000000000..8c34c033d --- /dev/null +++ b/src/type/pick/pick.ts @@ -0,0 +1,108 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF 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 { IndexedKeyResolve } from '../indexed/index' +import { Discard } from '../discard/index' +import { TransformKind } from '../symbols/index' +import { CloneType } from '../clone/type' +import { TIntersect as IsIntersectType, TUnion as IsUnionType, TObject as IsObjectType, TSchema as IsSchemaType } from '../guard/type' + +// ------------------------------------------------------------------ +// FromIntersect +// ------------------------------------------------------------------ +// prettier-ignore +type FromIntersect = + T extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? [PickResolve, ...FromIntersect] + : [] +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[]] + ? [PickResolve, ...FromUnion] + : [] +// 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 ( + IsIntersectType(T) ? Intersect(FromIntersect(T.allOf, K)) : + IsUnionType(T) ? Union(FromUnion(T.anyOf, K)) : + IsObjectType(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): 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: TSchema | readonly PropertyKey[], options: SchemaOptions = {}): any { + const I = IsSchemaType(K) ? IndexedKeyResolve(K) : (K as string[]) + const D = Discard(T, [TransformKind, '$id', 'required']) as TSchema + const R = CloneType(PickResolve(T, I), options) + return { ...D, ...R } +} diff --git a/src/type/promise/index.ts b/src/type/promise/index.ts new file mode 100644 index 000000000..55150952e --- /dev/null +++ b/src/type/promise/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './promise' diff --git a/src/type/promise/promise.ts b/src/type/promise/promise.ts new file mode 100644 index 000000000..5ee1d225f --- /dev/null +++ b/src/type/promise/promise.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 type { Static } from '../static/index' +import { Kind } from '../symbols/index' +import { CloneType } from '../clone/type' + +// ------------------------------------------------------------------ +// TPromise +// ------------------------------------------------------------------ +export interface TPromise extends TSchema { + [Kind]: 'Promise' + static: Promise> + type: 'Promise' + item: TSchema +} +/** `[JavaScript]` Creates a Promise type */ +export function Promise(item: T, options: SchemaOptions = {}): TPromise { + return { + ...options, + [Kind]: 'Promise', + type: 'Promise', + item: CloneType(item), + } as unknown as TPromise +} diff --git a/src/type/readonly-optional/index.ts b/src/type/readonly-optional/index.ts new file mode 100644 index 000000000..3e44a84c0 --- /dev/null +++ b/src/type/readonly-optional/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './readonly-optional' diff --git a/src/type/readonly-optional/readonly-optional.ts b/src/type/readonly-optional/readonly-optional.ts new file mode 100644 index 000000000..4f1ce9e8b --- /dev/null +++ b/src/type/readonly-optional/readonly-optional.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 type { TSchema } from '../schema/index' +import { type TReadonly, Readonly } from '../readonly/index' +import { type TOptional, Optional } from '../optional/index' + +// ------------------------------------------------------------------ +// TReadonlyOptional +// ------------------------------------------------------------------ +export type TReadonlyOptional = TOptional & TReadonly + +/** `[Json]` Creates a Readonly and Optional property */ +export function ReadonlyOptional(schema: T): TReadonly> { + return Readonly(Optional(schema)) +} diff --git a/src/type/readonly/index.ts b/src/type/readonly/index.ts new file mode 100644 index 000000000..ef48e5346 --- /dev/null +++ b/src/type/readonly/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' diff --git a/src/type/readonly/readonly.ts b/src/type/readonly/readonly.ts new file mode 100644 index 000000000..b925a5a84 --- /dev/null +++ b/src/type/readonly/readonly.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 type { TSchema } from '../schema/index' +import { ReadonlyKind } from '../symbols/index' +import { CloneType } from '../clone/type' + +// ------------------------------------------------------------------ +// TReadonly +// ------------------------------------------------------------------ +export type TReadonly = T & { [ReadonlyKind]: 'Readonly' } + +/** `[Json]` Creates a Readonly property */ +export function Readonly(schema: T): TReadonly { + return { ...CloneType(schema), [ReadonlyKind]: 'Readonly' } +} diff --git a/src/type/record/index.ts b/src/type/record/index.ts new file mode 100644 index 000000000..10eb1ceb9 --- /dev/null +++ b/src/type/record/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './record' diff --git a/src/type/record/record.ts b/src/type/record/record.ts new file mode 100644 index 000000000..c38805eac --- /dev/null +++ b/src/type/record/record.ts @@ -0,0 +1,196 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF 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 { type TObject, type TProperties, type TAdditionalProperties, type ObjectOptions, Object } from '../object/index' +import { type TLiteral, TLiteralValue } from '../literal/index' +import { type TNever, Never } from '../never/index' +import { type TUnion, Union } from '../union/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 { TTemplateLiteral, IsTemplateLiteralFinite, TemplateLiteralParseExact } from '../template-literal/index' +import { PatternStringExact, PatternNumberExact } from '../patterns/index' +import { IndexedKeyResolve } from '../indexed/index' +import { Kind, Hint } from '../symbols/index' +import { CloneType } from '../clone/type' +import { IsUndefined } from '../guard/value' +import { TUnion as IsUnionType, TTemplateLiteral as IsTemplateLiteralType, TLiteral as IsLiteralType, TString as IsStringType, TInteger as IsIntegerType, TNumber as IsNumberType } from '../guard/type' + +// ------------------------------------------------------------------ +// FromPattern | FromKeys +// ------------------------------------------------------------------ +// prettier-ignore +function FromPattern(pattern: string, T: TSchema) { + return { [Kind]: 'Record', type: 'object', patternProperties: { [pattern]: CloneType(T) } } +} +// prettier-ignore +function FromKeys(keys: string[], T: TSchema) { + const properties = keys.reduce((acc, key) => ({ ...acc, [key]: CloneType(T) }), {} as TProperties) + return Object(properties, { [Hint]: 'Record' }) +} +// ------------------------------------------------------------------ +// FromTemplateLiteralKey (Fast Inference) +// ------------------------------------------------------------------ +// prettier-ignore +type FromTemplateLiteralKeyInfinite = Ensure> +// prettier-ignore +type FromTemplateLiteralKeyFinite> = ( + Ensure>> +) +// prettier-ignore +type FromTemplateLiteralKey = IsTemplateLiteralFinite extends false + ? FromTemplateLiteralKeyInfinite + : FromTemplateLiteralKeyFinite +// prettier-ignore +function FromTemplateLiteralKey(K: K, T: T): FromTemplateLiteralKey { + const expression = TemplateLiteralParseExact(K.pattern) + return ( + IsTemplateLiteralFinite(expression) + ? FromKeys(IndexedKeyResolve(K), T) + : FromPattern(K.pattern, T) + ) as FromTemplateLiteralKey +} +// ------------------------------------------------------------------ +// FromEnumKey (Special Case) +// ------------------------------------------------------------------ +// prettier-ignore +type FromEnumKey, T extends TSchema> = Ensure> +// ------------------------------------------------------------------ +// FromUnionKey +// ------------------------------------------------------------------ +// prettier-ignore +type FromUnionKeyLiteralString, T extends TSchema> = { [_ in K['const']]: T } +// prettier-ignore +type FromUnionKeyLiteralNumber, T extends TSchema> = { [_ in K['const']]: T } +// prettier-ignore +type FromUnionKeyRest = + K extends [infer L extends TSchema, ...infer R extends TSchema[]] ? ( + L extends TUnion ? FromUnionKeyRest & FromUnionKeyRest : + L extends TLiteral ? FromUnionKeyLiteralString & FromUnionKeyRest : + L extends TLiteral ? FromUnionKeyLiteralNumber & FromUnionKeyRest : + {}) : {} +// prettier-ignore +type FromUnionKey> = ( + Ensure> +) +// prettier-ignore +function FromUnionKey(K: K, T: T): FromUnionKey { + return FromKeys(IndexedKeyResolve(Union(K)), T) as FromUnionKey +} +// ------------------------------------------------------------------ +// FromLiteralKey +// ------------------------------------------------------------------ +// prettier-ignore +type FromLiteralKey = ( + Ensure]: T }>> +) +// prettier-ignore +function FromLiteralKey(K: K, T: T): FromLiteralKey { + return FromKeys([(K as string).toString()], T) as FromLiteralKey +} +// ------------------------------------------------------------------ +// FromStringKey +// ------------------------------------------------------------------ +// prettier-ignore +type FromStringKey<_ extends TString, T extends TSchema> = ( + Ensure> +) +// prettier-ignore +function FromStringKey(K: K, T: T): FromStringKey { + const pattern = IsUndefined(K.pattern) ? PatternStringExact : K.pattern + return FromPattern(pattern, T) as FromStringKey +} +// ------------------------------------------------------------------ +// FromIntegerKey +// ------------------------------------------------------------------ +// prettier-ignore +type FromIntegerKey<_ extends TSchema, T extends TSchema> = ( + Ensure> +) +// prettier-ignore +function FromIntegerKey(_: K, T: T): FromIntegerKey { + return FromPattern(PatternNumberExact, T) as FromIntegerKey +} +// ------------------------------------------------------------------ +// FromNumberKey +// ------------------------------------------------------------------ +// prettier-ignore +type FromNumberKey<_ extends TSchema, T extends TSchema> = ( + Ensure> +) +// prettier-ignore +function FromNumberKey(_: K, T: T): FromIntegerKey { + return FromPattern(PatternNumberExact, T) as FromIntegerKey +} +// ------------------------------------------------------------------ +// RecordResolve +// ------------------------------------------------------------------ +// prettier-ignore +export type RecordResolve = + K extends TEnum ? FromEnumKey : // (Special: Ensure resolve Enum before Union) + K extends TUnion ? FromUnionKey : + K extends TTemplateLiteral ? FromTemplateLiteralKey : + K extends TLiteral ? FromLiteralKey : + K extends TInteger ? FromIntegerKey : + K extends TNumber ? FromNumberKey : + K extends TString ? FromStringKey : + TNever +// prettier-ignore +export function RecordResolve(K: K, T: T): RecordResolve { + return ( + IsUnionType(K) ? FromUnionKey(K.anyOf, T) : + IsTemplateLiteralType(K) ? FromTemplateLiteralKey(K, T) : + IsLiteralType(K) ? FromLiteralKey(K.const, T) : + IsStringType(K) ? FromStringKey(K, T) : + IsIntegerType(K) ? FromIntegerKey(K, T) : + IsNumberType(K) ? FromNumberKey(K, T) : + Never() + ) as unknown as RecordResolve +} + +// ------------------------------------------------------------------ +// TRecord +// ------------------------------------------------------------------ +// prettier-ignore +export interface TRecord extends TSchema { + [Kind]: 'Record' + static: Record, string | number>, Static> + type: 'object' + patternProperties: { [pattern: string]: T } + additionalProperties: TAdditionalProperties +} +// prettier-ignore +/** `[Json]` Creates a Record type */ +export function Record(K: K, T: T, options: ObjectOptions = {}): RecordResolve { + return CloneType(RecordResolve(K, T), options) +} diff --git a/src/type/recursive/index.ts b/src/type/recursive/index.ts new file mode 100644 index 000000000..9bb19d9f4 --- /dev/null +++ b/src/type/recursive/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './recursive' diff --git a/src/type/recursive/recursive.ts b/src/type/recursive/recursive.ts new file mode 100644 index 000000000..eda0ef279 --- /dev/null +++ b/src/type/recursive/recursive.ts @@ -0,0 +1,66 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF 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 +} +// ------------------------------------------------------------------ +// RecursiveResolve +// ------------------------------------------------------------------ +export type RecursiveResolve = Static]> +// ------------------------------------------------------------------ +// TRecursive +// ------------------------------------------------------------------ +export interface TRecursive extends TSchema { + [Hint]: 'Recursive' + static: RecursiveResolve +} +// 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 + return CloneType({ + ...options, + [Hint]: 'Recursive', + ...thisType, + }) as unknown as TRecursive +} diff --git a/src/type/ref/index.ts b/src/type/ref/index.ts new file mode 100644 index 000000000..1db9fa4e0 --- /dev/null +++ b/src/type/ref/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './ref' diff --git a/src/type/ref/ref.ts b/src/type/ref/ref.ts new file mode 100644 index 000000000..8aacc2381 --- /dev/null +++ b/src/type/ref/ref.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, SchemaOptions } from '../schema/index' +import type { Static } from '../static/index' +import { Kind } from '../symbols/index' +import { IsString, IsUndefined } from '../guard/value' + +// ------------------------------------------------------------------ +// TRef +// ------------------------------------------------------------------ +export interface TRef extends TSchema { + [Kind]: 'Ref' + static: Static + $ref: string +} +/** `[Json]` Creates a Ref type. The referenced type must contain a $id */ +export function Ref(schema: T, options?: SchemaOptions): TRef +/** `[Json]` Creates a Ref type. */ +export function Ref($ref: string, options?: SchemaOptions): TRef +/** `[Json]` Creates a Ref type. */ +export function Ref(unresolved: TSchema | string, options: SchemaOptions = {}) { + if (IsString(unresolved)) return { ...options, [Kind]: 'Ref', $ref: unresolved } + if (IsUndefined(unresolved.$id)) throw new Error('Reference target type must specify an $id') + return { + ...options, + [Kind]: 'Ref', + $ref: unresolved.$id!, + } +} diff --git a/src/type/regexp/index.ts b/src/type/regexp/index.ts new file mode 100644 index 000000000..00fab30b4 --- /dev/null +++ b/src/type/regexp/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './regexp' diff --git a/src/type/regexp/regexp.ts b/src/type/regexp/regexp.ts new file mode 100644 index 000000000..7db782682 --- /dev/null +++ b/src/type/regexp/regexp.ts @@ -0,0 +1,47 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF 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 { TString } from '../string/index' +import { IsString } from '../guard/value' +import { Kind } from '../symbols/index' + +// ------------------------------------------------------------------ +// TRegExp +// ------------------------------------------------------------------ +export type TRegExp = TString + +/** `[JavaScript]` Creates a String type from a Regular Expression pattern */ +export function RegExp(pattern: string, options?: SchemaOptions): TRegExp +/** `[JavaScript]` Creates a String type from a Regular Expression */ +export function RegExp(regex: RegExp, options?: SchemaOptions): TRegExp +/** `[JavaScript]` Creates a String type */ +export function RegExp(unresolved: string | RegExp, options: SchemaOptions = {}) { + const pattern = IsString(unresolved) ? unresolved : unresolved.source + return { ...options, [Kind]: 'String', type: 'string', pattern } as TRegExp +} diff --git a/src/type/registry/format.ts b/src/type/registry/format.ts new file mode 100644 index 000000000..ebcf3d74a --- /dev/null +++ b/src/type/registry/format.ts @@ -0,0 +1,55 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export type FormatRegistryValidationFunction = (value: string) => boolean +/** A registry for user defined string formats */ +const map = new Map() +/** Returns the entries in this registry */ +export function Entries() { + return new Map(map) +} +/** Clears all user defined string formats */ +export function Clear() { + return map.clear() +} +/** Deletes a registered format */ +export function Delete(format: string) { + return map.delete(format) +} +/** Returns true if the user defined string format exists */ +export function Has(format: string) { + return map.has(format) +} +/** Sets a validation function for a user defined string format */ +export function Set(format: string, func: FormatRegistryValidationFunction) { + map.set(format, func) +} +/** Gets a validation function for a user defined string format */ +export function Get(format: string) { + return map.get(format) +} diff --git a/src/type/registry/index.ts b/src/type/registry/index.ts new file mode 100644 index 000000000..a0fb0e043 --- /dev/null +++ b/src/type/registry/index.ts @@ -0,0 +1,30 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * as FormatRegistry from './format' +export * as TypeRegistry from './type' diff --git a/src/type/registry/type.ts b/src/type/registry/type.ts new file mode 100644 index 000000000..78e313b4a --- /dev/null +++ b/src/type/registry/type.ts @@ -0,0 +1,56 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export type TypeRegistryValidationFunction = (schema: TSchema, value: unknown) => boolean +/** A registry for user defined types */ + +const map = new Map>() +/** Returns the entries in this registry */ +export function Entries() { + return new Map(map) +} +/** Clears all user defined types */ +export function Clear() { + return map.clear() +} +/** Deletes a registered type */ +export function Delete(kind: string) { + return map.delete(kind) +} +/** Returns true if this registry contains this kind */ +export function Has(kind: string) { + return map.has(kind) +} +/** Sets a validation function for a user defined type */ +export function Set(kind: string, func: TypeRegistryValidationFunction) { + map.set(kind, func) +} +/** Gets a custom validation function for a user defined type */ +export function Get(kind: string) { + return map.get(kind) +} diff --git a/src/type/required/index.ts b/src/type/required/index.ts new file mode 100644 index 000000000..74bb43b44 --- /dev/null +++ b/src/type/required/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 './required' diff --git a/src/type/required/required.ts b/src/type/required/required.ts new file mode 100644 index 000000000..761f0096e --- /dev/null +++ b/src/type/required/required.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 { TSchema, SchemaOptions } from '../schema/index' +import type { Evaluate } from '../helpers/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 { TIntersect as IsIntersectType, TUnion as IsUnionType, TObject as IsObjectType } from '../guard/type' + +// ------------------------------------------------------------------ +// FromIntersect +// ------------------------------------------------------------------ +// prettier-ignore +type FromIntersect = ( + T extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? [RequiredResolve, ...FromIntersect] + : [] +) +// prettier-ignore +function FromIntersect(T: [...T]) : FromIntersect { + const [L, ...R] = T + return ( + T.length > 0 + ? [RequiredResolve(L), ...FromIntersect(R)] + : [] + ) as FromIntersect +} +// ------------------------------------------------------------------ +// FromUnion +// ------------------------------------------------------------------ +// prettier-ignore +type FromUnion = ( + T extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? [RequiredResolve, ...FromUnion] + : [] +) +// prettier-ignore +function FromUnion(T: [...T]): FromUnion { + const [L, ...R] = T + return ( + T.length > 0 + ? [RequiredResolve(L), ...FromUnion(R)] + : [] + ) as FromUnion +} +// ------------------------------------------------------------------ +// FromProperties +// ------------------------------------------------------------------ +// prettier-ignore +type FromProperties = 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 +export type RequiredResolve = ( + T extends TRecursive ? TRecursive> : + T extends TIntersect ? TIntersect> : + T extends TUnion ? TUnion> : + T extends TObject ? TObject> : + TObject<{}> +) +// prettier-ignore +export function RequiredResolve(T: T): RequiredResolve { + return ( + IsIntersectType(T) ? Intersect(FromIntersect(T.allOf)) : + IsUnionType(T) ? Union(FromUnion(T.anyOf)) : + IsObjectType(T) ? Object(FromProperties(T.properties)) : + Object({}) + ) as RequiredResolve +} +// ------------------------------------------------------------------ +// TRequired +// ------------------------------------------------------------------ +export type TRequired = RequiredResolve + +/** `[Json]` Constructs a type where all properties are required */ +export function Required(T: T, options: SchemaOptions = {}): TRequired { + const D = Discard(T, [TransformKind, '$id', 'required']) as TSchema + const R = CloneType(RequiredResolve(T) as any, options) + return { ...D, ...R } +} diff --git a/src/type/rest/index.ts b/src/type/rest/index.ts new file mode 100644 index 000000000..b1442f480 --- /dev/null +++ b/src/type/rest/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './rest' diff --git a/src/type/rest/rest.ts b/src/type/rest/rest.ts new file mode 100644 index 000000000..6582bf84b --- /dev/null +++ b/src/type/rest/rest.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 } 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' +import { TIntersect as IsIntersectType, TUnion as IsUnionType, TTuple as IsTupleType } from '../guard/type' + +// ------------------------------------------------------------------ +// RestResolve +// ------------------------------------------------------------------ +// prettier-ignore +export type RestResolve = + T extends TIntersect ? [...S] : + T extends TUnion ? [...S] : + T extends TTuple ? [...S] : + [] +export function RestResolve(T: T) { + return (IsIntersectType(T) ? [...T.allOf] : IsUnionType(T) ? [...T.anyOf] : IsTupleType(T) ? [...(T.items ?? [])] : []) as RestResolve +} +// ------------------------------------------------------------------ +// TRest +// ------------------------------------------------------------------ +export type TRest = RestResolve + +/** `[Json]` Extracts interior Rest elements from Tuple, Intersect and Union types */ +export function Rest(T: T): TRest { + return CloneRest(RestResolve(T)) +} diff --git a/src/type/return-type/index.ts b/src/type/return-type/index.ts new file mode 100644 index 000000000..610d3adc0 --- /dev/null +++ b/src/type/return-type/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './return-type' diff --git a/src/type/return-type/return-type.ts b/src/type/return-type/return-type.ts new file mode 100644 index 000000000..0df3d4bbf --- /dev/null +++ b/src/type/return-type/return-type.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 type { SchemaOptions } from '../schema/index' +import type { TFunction } from '../function/index' +import { CloneType } from '../clone/type' + +// ------------------------------------------------------------------ +// TReturnType +// ------------------------------------------------------------------ +export type TReturnType = T['returns'] + +/** `[JavaScript]` Extracts the ReturnType from the given Function type */ +export function ReturnType>(schema: T, options: SchemaOptions = {}): TReturnType { + return CloneType(schema.returns, options) +} diff --git a/src/type/schema/anyschema.ts b/src/type/schema/anyschema.ts new file mode 100644 index 000000000..8f646e823 --- /dev/null +++ b/src/type/schema/anyschema.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. + +---------------------------------------------------------------------------*/ + +// ------------------------------------------------------------------ +// 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 { 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' + +// ------------------------------------------------------------------ +// 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 diff --git a/src/type/schema/index.ts b/src/type/schema/index.ts new file mode 100644 index 000000000..bfd714bb5 --- /dev/null +++ b/src/type/schema/index.ts @@ -0,0 +1,30 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './anyschema' +export * from './schema' diff --git a/src/type/schema/schema.ts b/src/type/schema/schema.ts new file mode 100644 index 000000000..45e276f81 --- /dev/null +++ b/src/type/schema/schema.ts @@ -0,0 +1,61 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF 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' + +// ------------------------------------------------------------------ +// 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 { + [ReadonlyKind]?: string + [OptionalKind]?: string + [Hint]?: string + params: unknown[] + static: unknown +} diff --git a/src/type/static/index.ts b/src/type/static/index.ts new file mode 100644 index 000000000..8f4f0c9dc --- /dev/null +++ b/src/type/static/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './static' diff --git a/src/type/static/static.ts b/src/type/static/static.ts new file mode 100644 index 000000000..55c3b9703 --- /dev/null +++ b/src/type/static/static.ts @@ -0,0 +1,92 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF 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[]] + ? [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 +) +// ------------------------------------------------------------------ +// Static +// ------------------------------------------------------------------ +/** Creates an decoded static type from a TypeBox type */ +export type StaticDecode = Static, P> +/** Creates an encoded static type from a TypeBox type */ +export type StaticEncode = Static +/** Creates a static type from a TypeBox type */ +export type Static = (T & { params: P })['static'] diff --git a/src/type/strict/index.ts b/src/type/strict/index.ts new file mode 100644 index 000000000..caf80eab1 --- /dev/null +++ b/src/type/strict/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './strict' diff --git a/src/type/strict/strict.ts b/src/type/strict/strict.ts new file mode 100644 index 000000000..9e232424e --- /dev/null +++ b/src/type/strict/strict.ts @@ -0,0 +1,34 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import type { TSchema } from '../schema/index' + +/** `[Json]` Omits compositing symbols from this schema */ +export function Strict(schema: T): T { + return JSON.parse(JSON.stringify(schema)) +} diff --git a/src/type/string/index.ts b/src/type/string/index.ts new file mode 100644 index 000000000..1c188b4fd --- /dev/null +++ b/src/type/string/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './string' diff --git a/src/type/string/string.ts b/src/type/string/string.ts new file mode 100644 index 000000000..5bcda7176 --- /dev/null +++ b/src/type/string/string.ts @@ -0,0 +1,86 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +import { TSchema, SchemaOptions } from '../schema/index' +import { Kind } from '../symbols/index' + +// ------------------------------------------------------------------ +// TString +// ------------------------------------------------------------------ +export type StringFormatOption = + | 'date-time' + | 'time' + | 'date' + | 'email' + | 'idn-email' + | 'hostname' + | 'idn-hostname' + | 'ipv4' + | 'ipv6' + | 'uri' + | 'uri-reference' + | 'iri' + | 'uuid' + | 'iri-reference' + | 'uri-template' + | 'json-pointer' + | 'relative-json-pointer' + | 'regex' + | ({} & string) +// prettier-ignore +export type StringContentEncodingOption = + | '7bit' + | '8bit' + | 'binary' + | 'quoted-printable' + | 'base64' + | ({} & string) +export interface StringOptions extends SchemaOptions { + /** The maximum string length */ + maxLength?: number + /** The minimum string length */ + minLength?: number + /** A regular expression pattern this string should match */ + pattern?: string + /** A format this string should match */ + format?: StringFormatOption + /** The content encoding for this string */ + contentEncoding?: StringContentEncodingOption + /** The content media type for this string */ + contentMediaType?: string +} +export interface TString extends TSchema, StringOptions { + [Kind]: 'String' + static: string + type: 'string' +} + +/** `[Json]` Creates a String type */ +export function String(options: StringOptions = {}): TString { + return { ...options, [Kind]: 'String', type: 'string' } as unknown as TString +} diff --git a/src/type/symbol/index.ts b/src/type/symbol/index.ts new file mode 100644 index 000000000..abd306265 --- /dev/null +++ b/src/type/symbol/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './symbol' diff --git a/src/type/symbol/symbol.ts b/src/type/symbol/symbol.ts new file mode 100644 index 000000000..f277df406 --- /dev/null +++ b/src/type/symbol/symbol.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 { TSchema, SchemaOptions } from '../schema/index' +import { Kind } from '../symbols/index' + +// ------------------------------------------------------------------ +// TSymbol +// ------------------------------------------------------------------ +export type SymbolValue = string | number | undefined +export interface TSymbol extends TSchema, SchemaOptions { + [Kind]: 'Symbol' + static: symbol + type: 'symbol' +} +/** `[JavaScript]` Creates a Symbol type */ +export function Symbol(options?: SchemaOptions): TSymbol { + return { ...options, [Kind]: 'Symbol', type: 'symbol' } as unknown as TSymbol +} diff --git a/src/type/symbols/index.ts b/src/type/symbols/index.ts new file mode 100644 index 000000000..bac16a322 --- /dev/null +++ b/src/type/symbols/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './symbols' diff --git a/src/type/symbols/symbols.ts b/src/type/symbols/symbols.ts new file mode 100644 index 000000000..dbb90a99a --- /dev/null +++ b/src/type/symbols/symbols.ts @@ -0,0 +1,38 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +/** Symbol key applied to transform types */ +export const TransformKind = Symbol.for('TypeBox.Transform') +/** Symbol key applied to readonly types */ +export const ReadonlyKind = Symbol.for('TypeBox.Readonly') +/** Symbol key applied to optional types */ +export const OptionalKind = Symbol.for('TypeBox.Optional') +/** Symbol key applied to types */ +export const Hint = Symbol.for('TypeBox.Hint') +/** Symbol key applied to types */ +export const Kind = Symbol.for('TypeBox.Kind') diff --git a/src/type/template-literal/finite.ts b/src/type/template-literal/finite.ts new file mode 100644 index 000000000..514b2c472 --- /dev/null +++ b/src/type/template-literal/finite.ts @@ -0,0 +1,109 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF 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, 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 './parser' + +// ------------------------------------------------------------------ +// TemplateLiteralFiniteError +// ------------------------------------------------------------------ +export class TemplateLiteralFiniteError extends Error {} + +// ------------------------------------------------------------------ +// 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 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 +type IsTemplateLiteralFiniteArray = + T extends [infer L extends TTemplateLiteralKind, ...infer R extends TTemplateLiteralKind[]] + ? IsTemplateLiteralFiniteCheck extends false + ? false + : IsTemplateLiteralFiniteArray : + true +// ------------------------------------------------------------------ +// IsTemplateLiteralFinite +// ------------------------------------------------------------------ +// prettier-ignore +export type IsTemplateLiteralFinite = + T extends TTemplateLiteral + ? IsTemplateLiteralFiniteArray + : false +// prettier-ignore +export function IsTemplateLiteralFinite(expression: Expression): boolean { + return ( + IsBooleanExpression(expression) ? true : + IsNumberExpression(expression) || IsStringExpression(expression) ? false : + (expression.type === 'and') ? expression.expr.every((expr) => IsTemplateLiteralFinite(expr)) : + (expression.type === 'or') ? expression.expr.every((expr) => IsTemplateLiteralFinite(expr)) : + (expression.type === 'const') ? true : + (() => { throw new TemplateLiteralFiniteError(`Unknown expression type`) })() + ) +} diff --git a/src/type/template-literal/generate.ts b/src/type/template-literal/generate.ts new file mode 100644 index 000000000..8993de63b --- /dev/null +++ b/src/type/template-literal/generate.ts @@ -0,0 +1,126 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF 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, TTemplateLiteralKind } from './index' +import type { TLiteral, TLiteralValue } from '../literal/index' +import type { Expression, ExpressionAnd, ExpressionOr, ExpressionConst } from './parser' +import type { TUnion } from '../union/index' + +// ------------------------------------------------------------------ +// StringReducers +// ------------------------------------------------------------------ +// StringReduceUnary<"A", ["B", "C"]> -> ["AB", "AC"] +// prettier-ignore +type StringReduceUnary = + R extends [infer A extends string, ...infer B extends string[]] + ? [`${L}${A}`, ...StringReduceUnary] + : [] + +// StringReduceBinary<['A', 'B'], ['C', 'D']> -> ["AC", "AD", "BC", "BD"] +// prettier-ignore +type StringReduceBinary = + L extends [infer A extends string, ...infer B extends string[]] + ? [...StringReduceUnary, ...StringReduceBinary] + : [] + +// StringReduceMany<[['A', 'B'], ['C', 'D'], ['E']]> -> [["ACE", "ADE", "BCE", "BDE"]] +// prettier-ignore +type StringReduceMany = + T extends [infer L extends string[], infer R extends string[], ...infer Rest extends string[][]] + ? StringReduceMany<[StringReduceBinary, ...Rest]> + : T + +// Reduce<[['A', 'B'], ['C', 'D'], ['E']]> -> ["ACE", "ADE", "BCE", "BDE"] +// prettier-ignore +type StringReduce> = + 0 extends keyof O + ? O[0] + : [] +// prettier-ignore +type TemplateLiteralReduceUnion = + T extends [infer L extends TLiteral, ...infer R extends TLiteral[]] + ? [L['const'], ...TemplateLiteralReduceUnion] + : [] +// prettier-ignore +type TemplateLiteralReduce = + T extends [infer L extends TTemplateLiteralKind, ...infer R extends TTemplateLiteralKind[]] + ? L extends TLiteral ? [[S], ...TemplateLiteralReduce] : + L extends TUnion + ? [TemplateLiteralReduceUnion, ...TemplateLiteralReduce] + : [] + : [] + +// ------------------------------------------------------------------ +// GenerateFromExpression +// ------------------------------------------------------------------ +// 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) => [...TemplateLiteralGenerate(expr)])) +} +// prettier-ignore +function* GenerateOr(expression: ExpressionOr): IterableIterator { + for (const expr of expression.expr) yield* TemplateLiteralGenerate(expr) +} +// prettier-ignore +function* GenerateConst(expression: ExpressionConst): IterableIterator { + return yield expression.const +} +// ------------------------------------------------------------------ +// TemplateLiteralGenerateError +// ------------------------------------------------------------------ +export class TemplateLiteralGenerateError extends Error {} +// ------------------------------------------------------------------ +// TemplateLiteralGenerate +// ------------------------------------------------------------------ +// prettier-ignore +export type TemplateLiteralGenerate = + T extends TTemplateLiteral + ? TemplateLiteralReduce extends infer R extends string[][] + ? StringReduce + : [] + : [] +export function* TemplateLiteralGenerate(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') + })() +} diff --git a/src/type/template-literal/index.ts b/src/type/template-literal/index.ts new file mode 100644 index 000000000..95f5d55aa --- /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 './parser' +export * from './pattern' +export * from './union' +export * from './template-literal' diff --git a/src/type/template-literal/parser.ts b/src/type/template-literal/parser.ts new file mode 100644 index 000000000..4af166869 --- /dev/null +++ b/src/type/template-literal/parser.ts @@ -0,0 +1,161 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +// ------------------------------------------------------------------ +// TemplateLiteralParserError +// ------------------------------------------------------------------ +export class TemplateLiteralParserError extends Error {} +// ------------------------------------------------------------------ +// 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 } + ) +} +/** Parses a pattern and returns an expression tree */ +// prettier-ignore +export function TemplateLiteralParse(pattern: string): Expression { + return ( + IsGroup(pattern) ? TemplateLiteralParse(InGroup(pattern)) : + IsPrecedenceOr(pattern) ? Or(pattern) : + IsPrecedenceAnd(pattern) ? And(pattern) : + { type: 'const', const: pattern } + ) +} + +/** Parses a pattern and strips forward and trailing ^ and $ */ +// prettier-ignore +export function TemplateLiteralParseExact(pattern: string): Expression { + return TemplateLiteralParse(pattern.slice(1, pattern.length - 1)) +} diff --git a/src/type/template-literal/pattern.ts b/src/type/template-literal/pattern.ts new file mode 100644 index 000000000..a0efcb7c2 --- /dev/null +++ b/src/type/template-literal/pattern.ts @@ -0,0 +1,71 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF 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 { + TTemplateLiteral as IsTemplateLiteralType, + TUnion as IsUnionType, + TNumber as IsNumberType, + TInteger as IsIntegerType, + TBigInt as IsBigIntType, + TString as IsStringType, + TLiteral as IsLiteralType, + TBoolean as IsBooleanType, +} from '../guard/type' + +// ------------------------------------------------------------------ +// TemplateLiteralPatternError +// ------------------------------------------------------------------ +export class TemplateLiteralPatternError extends Error {} + +// ------------------------------------------------------------------ +// TemplateLiteralPattern +// ------------------------------------------------------------------ +function Escape(value: string) { + return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') +} +// prettier-ignore +function Visit(schema: TSchema, acc: string): string { + return ( + IsTemplateLiteralType(schema) ? schema.pattern.slice(1, schema.pattern.length - 1) : + IsUnionType(schema) ? `(${schema.anyOf.map((schema) => Visit(schema, acc)).join('|')})` : + IsNumberType(schema) ? `${acc}${PatternNumber}` : + IsIntegerType(schema) ? `${acc}${PatternNumber}` : + IsBigIntType(schema) ? `${acc}${PatternNumber}` : + IsStringType(schema) ? `${acc}${PatternString}` : + IsLiteralType(schema) ? `${acc}${Escape(schema.const.toString())}` : + IsBooleanType(schema) ? `${acc}${PatternBoolean}` : + (() => { throw new TemplateLiteralPatternError(`Unexpected Kind '${schema[Kind]}'`) })() + ) +} +export function TemplateLiteralPattern(kinds: TTemplateLiteralKind[]): string { + return `^${kinds.map((schema) => Visit(schema, '')).join('')}\$` +} diff --git a/src/type/template-literal/syntax.ts b/src/type/template-literal/syntax.ts new file mode 100644 index 000000000..d45a271cc --- /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 { Ensure, Assert, Trim } from '../helpers/index' +import type { TTemplateLiteralKind, TTemplateLiteral } from './index' +import { type TLiteral, Literal } from '../literal/index' +import { type TBoolean, Boolean } from '../boolean/index' +import { type TBigInt, BigInt } from '../bigint/index' +import { type TNumber, Number } from '../number/index' +import { type TString, String } from '../string/index' +import { Union, UnionResolve } 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] : + Union(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 = UnionResolve> +// 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] : + [] + +// ------------------------------------------------------------------ +// TemplateLiteralSyntax +// ------------------------------------------------------------------ +// prettier-ignore +export type TemplateLiteralSyntax = Ensure, TTemplateLiteralKind[]>>> + +// prettier-ignore +/** Parses TemplateLiteralSyntax and returns a tuple of TemplateLiteralKinds */ +export function TemplateLiteralSyntax(syntax: string): TTemplateLiteralKind[] { + return [...FromSyntax(syntax)] +} diff --git a/src/type/template-literal/template-literal.ts b/src/type/template-literal/template-literal.ts new file mode 100644 index 000000000..5599cc516 --- /dev/null +++ b/src/type/template-literal/template-literal.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 { 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 { TemplateLiteralPattern } from './pattern' +import { TemplateLiteralSyntax } from './syntax' +import { EmptyString } from '../helpers/index' +import { IsString } from '../guard/value' +import { Kind } from '../symbols/index' + +// ------------------------------------------------------------------ +// TemplateLiteralResolve +// ------------------------------------------------------------------ +// prettier-ignore +type TemplateLiteralResolveConst = + T extends TUnion ? { [K in keyof U]: TemplateLiteralResolve, 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 TemplateLiteralResolve = + T extends [infer L, ...infer R] ? `${TemplateLiteralResolveConst}${TemplateLiteralResolve, 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: TemplateLiteralResolve + 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): TemplateLiteralSyntax +/** `[Json]` Creates a TemplateLiteral type */ +export function TemplateLiteral(kinds: [...T], options?: SchemaOptions): TTemplateLiteral +/** `[Json]` Creates a TemplateLiteral type */ +// prettier-ignore +export function TemplateLiteral(unresolved: TTemplateLiteralKind[] | string, options: SchemaOptions = {}) { + const pattern = IsString(unresolved) + ? TemplateLiteralPattern(TemplateLiteralSyntax(unresolved)) + : TemplateLiteralPattern(unresolved as TTemplateLiteralKind[]) + return { ...options, [Kind]: 'TemplateLiteral', type: 'string', pattern } +} diff --git a/src/type/template-literal/union.ts b/src/type/template-literal/union.ts new file mode 100644 index 000000000..58f42bcb1 --- /dev/null +++ b/src/type/template-literal/union.ts @@ -0,0 +1,47 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF 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 { type TUnion, Union } from '../union/index' +import { type TLiteral, Literal } from '../literal/index' +import { type TString, String } from '../string/index' +import { type TNever } from '../never/index' +import { TemplateLiteralGenerate } from './generate' +import { TemplateLiteralParseExact } from './parser' +import { IsTemplateLiteralFinite } from './finite' + +// ------------------------------------------------------------------ +// TemplateLiteralToUnion +// ------------------------------------------------------------------ +/** Resolves a template literal as a TUnion */ +export function TemplateLiteralToUnion(template: TTemplateLiteral): TNever | TString | TUnion { + const expression = TemplateLiteralParseExact(template.pattern) + if (!IsTemplateLiteralFinite(expression)) return String() + const literals = [...TemplateLiteralGenerate(expression)].map((value) => Literal(value)) + return Union(literals) +} diff --git a/src/type/transform/index.ts b/src/type/transform/index.ts new file mode 100644 index 000000000..4e6ab0218 --- /dev/null +++ b/src/type/transform/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './transform' diff --git a/src/type/transform/transform.ts b/src/type/transform/transform.ts new file mode 100644 index 000000000..a080451f9 --- /dev/null +++ b/src/type/transform/transform.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 { Static, StaticDecode } from '../static/index' + +import { TTransform as IsTransform } from '../guard/type' +import { CloneType } from '../clone/type' +import { TransformKind } from '../symbols/index' + +// ------------------------------------------------------------------ +// 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> + } +} +// ------------------------------------------------------------------ +// TTransform +// ------------------------------------------------------------------ +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 + [TransformKind]: TransformOptions + [key: string]: any +} +/** `[Json]` Creates a Transform type */ +export function Transform(schema: I): TransformDecodeBuilder { + return new TransformDecodeBuilder(schema) +} diff --git a/src/type/tuple/index.ts b/src/type/tuple/index.ts new file mode 100644 index 000000000..c1eb788d2 --- /dev/null +++ b/src/type/tuple/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './tuple' diff --git a/src/type/tuple/tuple.ts b/src/type/tuple/tuple.ts new file mode 100644 index 000000000..1f462cc49 --- /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' + +// ------------------------------------------------------------------ +// TupleResolve +// ------------------------------------------------------------------ +// prettier-ignore +export type TupleResolve = + T extends [infer L extends TSchema, ...infer R extends TSchema[]] + ? [Static, ...TupleResolve] + : [] +// ------------------------------------------------------------------ +// TTuple +// ------------------------------------------------------------------ +export interface TTuple extends TSchema { + [Kind]: 'Tuple' + static: TupleResolve + type: 'array' + items?: T + additionalItems?: false + minItems: number + maxItems: number +} +/** `[Json]` Creates a Tuple type */ +export function Tuple(items: [...T], options: SchemaOptions = {}): TTuple { + // return TupleResolver.Resolve(T) + const [additionalItems, minItems, maxItems] = [false, items.length, items.length] + // prettier-ignore + return ( + items.length > 0 ? + { ...options, [Kind]: 'Tuple', type: 'array', items: CloneRest(items), additionalItems, minItems, maxItems } : + { ...options, [Kind]: 'Tuple', type: 'array', minItems, maxItems } + ) as unknown as TTuple +} diff --git a/src/type/type/index.ts b/src/type/type/index.ts new file mode 100644 index 000000000..081b459d1 --- /dev/null +++ b/src/type/type/index.ts @@ -0,0 +1,43 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +// ------------------------------------------------------------------ +// JsonTypeBuilder +// ------------------------------------------------------------------ +export { JsonTypeBuilder } from './json' + +// ------------------------------------------------------------------ +// JavaScriptTypeBuilder +// ------------------------------------------------------------------ +import * as TypeBuilder from './type' +import { JavaScriptTypeBuilder } from './javascript' + +/** JavaScript Type Builder with Static Resolution for TypeScript */ +const Type = TypeBuilder as InstanceType +export { JavaScriptTypeBuilder } +export { Type } diff --git a/src/type/type/javascript.ts b/src/type/type/javascript.ts new file mode 100644 index 000000000..b45aeb229 --- /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, 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 Function(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 String type from a Regular Expression pattern */ + public RegExp(pattern: string, options?: SchemaOptions): TRegExp + /** `[JavaScript]` Creates a String type from a Regular Expression */ + public RegExp(regex: RegExp, options?: SchemaOptions): TRegExp + /** `[JavaScript]` Creates a String type */ + public RegExp(unresolved: string | RegExp, options: SchemaOptions = {}) { + return RegExp(unresolved as any, options) + } + /** `[JavaScript]` Extracts the ReturnType from the given Function type */ + public ReturnType>(schema: T, options: SchemaOptions = {}): TReturnType { + return ReturnType(schema, options) + } + /** `[JavaScript]` Creates a Symbol type */ + public Symbol(options?: SchemaOptions): TSymbol { + return Symbol(options) + } + /** `[JavaScript]` Creates a Undefined type */ + public Undefined(options: SchemaOptions = {}): TUndefined { + return Undefined(options) + } + /** `[JavaScript]` Creates a Uint8Array type */ + public Uint8Array(options: Uint8ArrayOptions = {}): TUint8Array { + return Uint8Array(options) + } + /** `[JavaScript]` Creates a Void type */ + public Void(options: SchemaOptions = {}): TVoid { + return Void(options) + } +} diff --git a/src/type/type/json.ts b/src/type/type/json.ts new file mode 100644 index 000000000..57fc9d21b --- /dev/null +++ b/src/type/type/json.ts @@ -0,0 +1,274 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF 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 } from '../exclude/index' +import { Extends, type TExtends } from '../extends/index' +import { Extract, type TExtract } from '../extract/index' +import { Index, TIndex, type IndexedKeyResolve } from '../indexed/index' +import { Integer, type IntegerOptions, type TInteger } from '../integer/index' +import { Intersect, type IntersectOptions, type IntersectResolve } from '../intersect/index' +import { Capitalize, Uncapitalize, Lowercase, Uppercase, type TCapitalize, type TUncapitalize, type TLowercase, type TUppercase } from '../intrinsic/index' +import { KeyOf, type TKeyOf } from '../keyof/index' +import { Literal, type TLiteral, type TLiteralValue } from '../literal/index' +import { Never, type TNever } from '../never/index' +import { Not, type TNot } from '../not/index' +import { Null, type TNull } from '../null/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 } from '../omit/index' +import { Optional, type TOptional } from '../optional/index' +import { Partial, type TPartial } from '../partial/index' +import { Pick, type TPick } from '../pick/index' +import { Readonly, type TReadonly } from '../readonly/index' +import { ReadonlyOptional, type TReadonlyOptional } from '../readonly-optional/index' +import { Record, type RecordResolve } from '../record/index' +import { Recursive, type TRecursive, type TThis } from '../recursive/index' +import { Ref, type TRef } from '../ref/index' +import { Required, type TRequired } 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 TemplateLiteralSyntax } from '../template-literal/index' +import { Transform, TransformDecodeBuilder } from '../transform/index' +import { Tuple, type TTuple } from '../tuple/index' +import { Union, type UnionResolve } 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): TReadonly { + return Readonly(schema) + } + /** `[Json]` Creates an Optional property */ + public Optional(schema: T): TOptional { + return Optional(schema) + } + // ------------------------------------------------------------------------ + // 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]` Creates a Conditional type */ + public Extends(left: L, right: R, trueType: T, falseType: U, options: SchemaOptions = {}): TExtends { + return Extends(left, right, trueType, falseType, 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 = {}): TExclude { + return Exclude(unionType, excludedMembers, 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 = {}): TExtract { + return Extract(type, union, options) + } + /** `[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 = {}): IntersectResolve { + return Intersect(T, options) + } + /** `[Json]` Creates a KeyOf type */ + public KeyOf(schema: T, options: SchemaOptions = {}): TKeyOf { + 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 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): 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(schema: T, options: ObjectOptions = {}): TPartial { + return Partial(schema, options) + } + /** `[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 = {}): RecordResolve { + 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(schema: T, options: SchemaOptions = {}): TRequired { + 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): TemplateLiteralSyntax + /** `[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 = {}): UnionResolve { + return Union(schemas, options) + } + /** `[Json]` Creates an Unknown type */ + public Unknown(options: SchemaOptions = {}): TUnknown { + return Unknown(options) + } + /** `[Json]` Creates a Unsafe type that will infers as the generic argument T */ + public Unsafe(options: UnsafeOptions = {}): TUnsafe { + return Unsafe(options) + } + /** `[Json]` Intrinsic function to Uppercase LiteralString types */ + public Uppercase(schema: T, options: SchemaOptions = {}): TUppercase { + return Uppercase(schema, options) + } +} diff --git a/src/type/type/type.ts b/src/type/type/type.ts new file mode 100644 index 000000000..53f275419 --- /dev/null +++ b/src/type/type/type.ts @@ -0,0 +1,89 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +// ------------------------------------------------------------------ +// Type: Module +// ------------------------------------------------------------------ +export { Any } from '../any/index' +export { Array } from '../array/index' +export { AsyncIterator } from '../async-iterator/index' +export { Awaited } from '../awaited/index' +export { BigInt } from '../bigint/index' +export { Boolean } from '../boolean/index' +export { Composite } from '../composite/index' +export { Const } from '../const/index' +export { Constructor } from '../constructor/index' +export { ConstructorParameters } from '../constructor-parameters/index' +export { Date } from '../date/index' +export { Deref } from '../deref/index' +export { Enum } from '../enum/index' +export { Exclude } from '../exclude/index' +export { Extends } from '../extends/index' +export { Extract } from '../extract/index' +export { Function } from '../function/index' +export { Index } from '../indexed/index' +export { InstanceType } from '../instance-type/index' +export { Integer } from '../integer/index' +export { Intersect } from '../intersect/index' +export { Capitalize, Uncapitalize, Lowercase, Uppercase } from '../intrinsic/index' +export { Iterator } from '../iterator/index' +export { KeyOf } from '../keyof/index' +export { Literal } from '../literal/index' +export { Mapped } from '../mapped/index' +export { Never } from '../never/index' +export { Not } from '../not/index' +export { Null } from '../null/index' +export { Number } from '../number/index' +export { Object } from '../object/index' +export { Omit } from '../omit/index' +export { Optional } from '../optional/index' +export { Parameters } from '../parameters/index' +export { Partial } from '../partial/index' +export { Pick } from '../pick/index' +export { Promise } from '../promise/index' +export { Readonly } from '../readonly/index' +export { ReadonlyOptional } from '../readonly-optional/index' +export { Record } from '../record/index' +export { Recursive } from '../recursive/index' +export { Ref } from '../ref/index' +export { RegExp } from '../regexp/index' +export { Required } from '../required/index' +export { Rest } from '../rest/index' +export { ReturnType } from '../return-type/index' +export { Strict } from '../strict/index' +export { String } from '../string/index' +export { Symbol } from '../symbol/index' +export { TemplateLiteral } from '../template-literal/index' +export { Transform } from '../transform/index' +export { Tuple } from '../tuple/index' +export { Uint8Array } from '../uint8array/index' +export { Undefined } from '../undefined/index' +export { Union } from '../union/index' +export { Unknown } from '../unknown/index' +export { Unsafe } from '../unsafe/index' +export { Void } from '../void/index' diff --git a/src/type/uint8array/index.ts b/src/type/uint8array/index.ts new file mode 100644 index 000000000..4c2296a42 --- /dev/null +++ b/src/type/uint8array/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './uint8array' diff --git a/src/type/uint8array/uint8array.ts b/src/type/uint8array/uint8array.ts new file mode 100644 index 000000000..095f7f7bb --- /dev/null +++ b/src/type/uint8array/uint8array.ts @@ -0,0 +1,47 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF 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' + +// ------------------------------------------------------------------ +// TUint8Array +// ------------------------------------------------------------------ +export interface Uint8ArrayOptions extends SchemaOptions { + maxByteLength?: number + minByteLength?: number +} +export interface TUint8Array extends TSchema, Uint8ArrayOptions { + [Kind]: 'Uint8Array' + static: Uint8Array + type: 'uint8array' +} +/** `[JavaScript]` Creates a Uint8Array type */ +export function Uint8Array(options: Uint8ArrayOptions = {}): TUint8Array { + return { ...options, [Kind]: 'Uint8Array', type: 'Uint8Array' } as unknown as TUint8Array +} diff --git a/src/type/undefined/index.ts b/src/type/undefined/index.ts new file mode 100644 index 000000000..cbc1c28e3 --- /dev/null +++ b/src/type/undefined/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './undefined' diff --git a/src/type/undefined/undefined.ts b/src/type/undefined/undefined.ts new file mode 100644 index 000000000..1e1e288e5 --- /dev/null +++ b/src/type/undefined/undefined.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 { Kind } from '../symbols/index' + +// ------------------------------------------------------------------ +// TUndefined +// ------------------------------------------------------------------ +export interface TUndefined extends TSchema { + [Kind]: 'Undefined' + static: undefined + type: 'undefined' +} +/** `[JavaScript]` Creates a Undefined type */ +export function Undefined(options: SchemaOptions = {}): TUndefined { + return { ...options, [Kind]: 'Undefined', type: 'undefined' } as unknown as TUndefined +} diff --git a/src/type/union/index.ts b/src/type/union/index.ts new file mode 100644 index 000000000..337cd4a00 --- /dev/null +++ b/src/type/union/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 './union' diff --git a/src/type/union/union.ts b/src/type/union/union.ts new file mode 100644 index 000000000..129cd4d7b --- /dev/null +++ b/src/type/union/union.ts @@ -0,0 +1,70 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF 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 TNever, Never } from '../never/index' +import { OptionalFromUnion } from '../modifiers/index' +import { CloneType, CloneRest } from '../clone/type' +import { Kind } from '../symbols/index' + +// ------------------------------------------------------------------ +// UnionResolve +// ------------------------------------------------------------------ +// prettier-ignore +export type UnionResolve = ( + T extends [] ? TNever : + T extends [TSchema] ? T[0] : + OptionalFromUnion +) +// prettier-ignore +export function UnionResolve(T: [...T]): UnionResolve { + return ( + T.length === 0 ? Never() : + T.length === 1 ? T[0] : + OptionalFromUnion(T) + ) as UnionResolve +} +// ------------------------------------------------------------------ +// TUnion +// ------------------------------------------------------------------ +// prettier-ignore +export interface TUnion extends TSchema { + [Kind]: 'Union' + static: { [K in keyof T]: T[K] extends TSchema ? Static : never }[number] + anyOf: T +} +/** `[Json]` Creates a Union type */ +// prettier-ignore +export function Union(T: [...T], options: SchemaOptions = {}): UnionResolve { + return ( + T.length === 0 ? Never(options) : + T.length === 1 ? CloneType(T[0], options) : + { ...options, [Kind]: 'Union', anyOf: CloneRest(T) + }) as UnionResolve +} diff --git a/src/type/unknown/index.ts b/src/type/unknown/index.ts new file mode 100644 index 000000000..7d9c7e293 --- /dev/null +++ b/src/type/unknown/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './unknown' diff --git a/src/type/unknown/unknown.ts b/src/type/unknown/unknown.ts new file mode 100644 index 000000000..cc1a7bc22 --- /dev/null +++ b/src/type/unknown/unknown.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' + +// ------------------------------------------------------------------ +// TUnknown +// ------------------------------------------------------------------ +export interface TUnknown extends TSchema { + [Kind]: 'Unknown' + static: unknown +} +/** `[Json]` Creates an Unknown type */ +export function Unknown(options: SchemaOptions = {}): TUnknown { + return { + ...options, + [Kind]: 'Unknown', + } as unknown as TUnknown +} diff --git a/src/type/unsafe/index.ts b/src/type/unsafe/index.ts new file mode 100644 index 000000000..78141b392 --- /dev/null +++ b/src/type/unsafe/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './unsafe' diff --git a/src/type/unsafe/unsafe.ts b/src/type/unsafe/unsafe.ts new file mode 100644 index 000000000..a60b33a41 --- /dev/null +++ b/src/type/unsafe/unsafe.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 { Kind } from '../symbols/index' + +// ------------------------------------------------------------------ +// TUnknown +// ------------------------------------------------------------------ +export interface UnsafeOptions extends SchemaOptions { + [Kind]?: string +} +export interface TUnsafe extends TSchema { + [Kind]: string + static: T +} +/** `[Json]` Creates a Unsafe type that will infers as the generic argument T */ +export function Unsafe(options: UnsafeOptions = {}): TUnsafe { + return { + ...options, + [Kind]: options[Kind] ?? 'Unsafe', + } as unknown as TUnsafe +} diff --git a/src/type/void/index.ts b/src/type/void/index.ts new file mode 100644 index 000000000..3b9bf5199 --- /dev/null +++ b/src/type/void/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './void' diff --git a/src/type/void/void.ts b/src/type/void/void.ts new file mode 100644 index 000000000..8d742b89b --- /dev/null +++ b/src/type/void/void.ts @@ -0,0 +1,47 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/type + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF 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' + +// ------------------------------------------------------------------ +// TVoid +// ------------------------------------------------------------------ +export interface TVoid extends TSchema { + [Kind]: 'Void' + static: void + type: 'void' +} +/** `[JavaScript]` Creates a Void type */ +export function Void(options: SchemaOptions = {}): TVoid { + return { + ...options, + [Kind]: 'Void', + type: 'void', + } as unknown as TVoid +} diff --git a/src/typebox.ts b/src/typebox.ts deleted file mode 100644 index 57e8cb27b..000000000 --- a/src/typebox.ts +++ /dev/null @@ -1,3410 +0,0 @@ -/*-------------------------------------------------------------------------- - -@sinclair/typebox - -The MIT License (MIT) - -Copyright (c) 2017-2023 Haydn Paterson (sinclair) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ----------------------------------------------------------------------------*/ - -// -------------------------------------------------------------------------- -// Symbols -// -------------------------------------------------------------------------- -export const Transform = Symbol.for('TypeBox.Transform') -export const Readonly = Symbol.for('TypeBox.Readonly') -export const Optional = Symbol.for('TypeBox.Optional') -export const Hint = Symbol.for('TypeBox.Hint') -export const Kind = Symbol.for('TypeBox.Kind') -// -------------------------------------------------------------------------- -// Patterns -// -------------------------------------------------------------------------- -export const PatternBoolean = '(true|false)' -export const PatternNumber = '(0|[1-9][0-9]*)' -export const PatternString = '(.*)' -export const PatternBooleanExact = `^${PatternBoolean}$` -export const PatternNumberExact = `^${PatternNumber}$` -export const PatternStringExact = `^${PatternString}$` -// -------------------------------------------------------------------------- -// Helpers -// -------------------------------------------------------------------------- -export type TupleToIntersect = T extends [infer I] ? I : T extends [infer I, ...infer R] ? I & TupleToIntersect : never -export type TupleToUnion = { [K in keyof T]: T[K] }[number] -export type UnionToIntersect = (U extends unknown ? (arg: U) => 0 : never) extends (arg: infer I) => 0 ? I : never -export type UnionLast = UnionToIntersect 0 : never> extends (x: infer L) => 0 ? L : never -export type UnionToTuple> = [U] extends [never] ? [] : [...UnionToTuple>, L] -export type Discard = T extends [infer L, ...infer R] ? (L extends D ? Discard : [L, ...Discard]) : [] -export type Flat = T extends [] ? [] : T extends [infer L] ? [...Flat] : T extends [infer L, ...infer R] ? [...Flat, ...Flat] : [T] -export type Trim = T extends `${' '}${infer U}` ? Trim : T extends `${infer U}${' '}` ? Trim : T -export type Assert = T extends E ? T : never -export type Evaluate = T extends infer O ? { [K in keyof O]: O[K] } : never -export type Ensure = T extends infer U ? U : never -// -------------------------------------------------------------------------- -// Type Assertions -// -------------------------------------------------------------------------- -export type AssertProperties = T extends TProperties ? T : TProperties -export type AssertRest = T extends E ? T : [] -export type AssertType = T extends E ? T : TNever -// -------------------------------------------------------------------------- -// Modifiers -// -------------------------------------------------------------------------- -export type TReadonlyOptional = TOptional & TReadonly -export type TReadonly = T & { [Readonly]: 'Readonly' } -export type TOptional = T & { [Optional]: 'Optional' } -// -------------------------------------------------------------------------- -// Readonly Unwrap -// -------------------------------------------------------------------------- -// prettier-ignore -export type ReadonlyUnwrapType = - T extends TReadonly ? ReadonlyUnwrapType : - T extends TOptional ? TOptional> : - T -// prettier-ignore -export type ReadonlyUnwrapRest = T extends [infer L, ...infer R] - ? L extends TReadonly - ? [ReadonlyUnwrapType>, ...ReadonlyUnwrapRest>] - : [L, ...ReadonlyUnwrapRest>] - : [] -// -------------------------------------------------------------------------- -// Optional Unwrap -// -------------------------------------------------------------------------- -// prettier-ignore -export type OptionalUnwrapType = - T extends TReadonly ? TReadonly> : - T extends TOptional ? OptionalUnwrapType : - T -// prettier-ignore -export type OptionalUnwrapRest = T extends [infer L, ...infer R] - ? L extends TOptional - ? [OptionalUnwrapType>, ...OptionalUnwrapRest>] - : [L, ...OptionalUnwrapRest>] - : [] -// -------------------------------------------------------------------------- -// IntersectType -// -------------------------------------------------------------------------- -// prettier-ignore -export type IntersectOptional = T extends [infer L, ...infer R] - ? L extends TOptional> - ? IntersectOptional> - : false - : true -// prettier-ignore -export type IntersectResolve>> = IntersectOptional> extends true - ? TOptional>> - : TIntersect> -// prettier-ignore -export type IntersectType = - T extends [] ? TNever : - T extends [TSchema] ? AssertType : - IntersectResolve -// -------------------------------------------------------------------------- -// UnionType -// -------------------------------------------------------------------------- -// prettier-ignore -export type UnionOptional = T extends [infer L, ...infer R] - ? L extends (TOptional>) - ? true - : UnionOptional> - : false -// prettier-ignore -export type UnionResolve>> = UnionOptional> extends true - ? TOptional>> - : TUnion> -// prettier-ignore -export type UnionType = - T extends [] ? TNever : - T extends [TSchema] ? AssertType : - UnionResolve -// -------------------------------------------------------------------------- -// TSchema -// -------------------------------------------------------------------------- -export interface SchemaOptions { - $schema?: string - /** Id for this schema */ - $id?: string - /** Title of this schema */ - title?: string - /** Description of this schema */ - description?: string - /** Default value for this schema */ - default?: any - /** Example values matching this schema */ - examples?: any - /** Optional annotation for readOnly */ - readOnly?: boolean - /** Optional annotation for writeOnly */ - writeOnly?: boolean - [prop: string]: any -} -export interface TKind { - [Kind]: string -} -export interface TSchema extends SchemaOptions, TKind { - [Readonly]?: string - [Optional]?: string - [Hint]?: string - params: unknown[] - static: unknown -} -// -------------------------------------------------------------------------- -// TAnySchema -// -------------------------------------------------------------------------- -export type TAnySchema = - | TSchema - | TAny - | TArray - | TAsyncIterator - | TBigInt - | TBoolean - | TConstructor - | TDate - | TEnum - | TFunction - | TInteger - | TIntersect - | TIterator - | TLiteral - | TNot - | TNull - | TNumber - | TObject - | TPromise - | TRecord - | TRef - | TString - | TSymbol - | TTemplateLiteral - | TThis - | TTuple - | TUndefined - | TUnion - | TUint8Array - | TUnknown - | TVoid -// -------------------------------------------------------------------------- -// TNumeric -// -------------------------------------------------------------------------- -export interface NumericOptions extends SchemaOptions { - exclusiveMaximum?: N - exclusiveMinimum?: N - maximum?: N - minimum?: N - multipleOf?: N -} -// -------------------------------------------------------------------------- -// TAny -// -------------------------------------------------------------------------- -export interface TAny extends TSchema { - [Kind]: 'Any' - static: any -} -// -------------------------------------------------------------------------- -// TArray -// -------------------------------------------------------------------------- -export interface ArrayOptions extends SchemaOptions { - /** The minimum number of items in this array */ - minItems?: number - /** The maximum number of items in this array */ - maxItems?: number - /** Should this schema contain unique items */ - uniqueItems?: boolean - /** A schema for which some elements should match */ - contains?: TSchema - /** A minimum number of contains schema matches */ - minContains?: number - /** A maximum number of contains schema matches */ - maxContains?: number -} -export interface TArray extends TSchema, ArrayOptions { - [Kind]: 'Array' - static: Static[] - type: 'array' - items: T -} -// -------------------------------------------------------------------------- -// TAsyncIterator -// -------------------------------------------------------------------------- -export interface TAsyncIterator extends TSchema { - [Kind]: 'AsyncIterator' - static: AsyncIterableIterator> - type: 'AsyncIterator' - items: T -} -// ------------------------------------------------------------------------------- -// TAwaited -// ------------------------------------------------------------------------------- -// prettier-ignore -export type TAwaitedRest = T extends [infer L, ...infer R] - ? [TAwaited>, ...TAwaitedRest>] - : [] -// prettier-ignore -export type TAwaited = - T extends TIntersect ? TIntersect> : - T extends TUnion ? TUnion> : - T extends TPromise ? TAwaited : - T -// -------------------------------------------------------------------------- -// TBigInt -// -------------------------------------------------------------------------- -export interface TBigInt extends TSchema, NumericOptions { - [Kind]: 'BigInt' - static: bigint - type: 'bigint' -} -// -------------------------------------------------------------------------- -// TBoolean -// -------------------------------------------------------------------------- -export interface TBoolean extends TSchema { - [Kind]: 'Boolean' - static: boolean - type: 'boolean' -} -// -------------------------------------------------------------------------- -// TConstructorParameters -// -------------------------------------------------------------------------- -export type TConstructorParameters> = Ensure> -// -------------------------------------------------------------------------- -// TInstanceType -// -------------------------------------------------------------------------- -export type TInstanceType> = T['returns'] -// -------------------------------------------------------------------------- -// TComposite -// -------------------------------------------------------------------------- -// prettier-ignore -export type TCompositeKeys = T extends [infer L, ...infer R] - ? keyof Assert['properties'] | TCompositeKeys> - : never -// prettier-ignore -export type TCompositeIndex, K extends string[]> = K extends [infer L, ...infer R] - ? { [_ in Assert]: TIndexType> } & TCompositeIndex> - : {} -// prettier-ignore -export type TCompositeReduce = UnionToTuple> extends infer K - ? Evaluate, Assert>> - : {} // ^ indexed via intersection of T -// prettier-ignore -export type TComposite = TIntersect extends TIntersect - ? TObject> - : TObject<{}> -// -------------------------------------------------------------------------- -// TConstructor -// -------------------------------------------------------------------------- -export type TConstructorReturnTypeResolve = Static -export type TConstructorParametersResolve = T extends [infer L extends TSchema, ...infer R extends TSchema[]] ? [Static, ...TFunctionParametersResolve] : [] -export type TConstructorResolve = Ensure) => TConstructorReturnTypeResolve> -export interface TConstructor extends TSchema { - [Kind]: 'Constructor' - static: TConstructorResolve - type: 'Constructor' - parameters: T - returns: U -} -// -------------------------------------------------------------------------- -// TDate -// -------------------------------------------------------------------------- -export interface DateOptions extends SchemaOptions { - /** The exclusive maximum timestamp value */ - exclusiveMaximumTimestamp?: number - /** The exclusive minimum timestamp value */ - exclusiveMinimumTimestamp?: number - /** The maximum timestamp value */ - maximumTimestamp?: number - /** The minimum timestamp value */ - minimumTimestamp?: number - /** The multiple of timestamp value */ - multipleOfTimestamp?: number -} -export interface TDate extends TSchema, DateOptions { - [Kind]: 'Date' - static: Date - type: 'date' -} -// -------------------------------------------------------------------------- -// TEnum -// -------------------------------------------------------------------------- -export type TEnumRecord = Record -export type TEnumValue = string | number -export type TEnumKey = string -export interface TEnum = Record> extends TSchema { - [Kind]: 'Union' - [Hint]: 'Enum' - static: T[keyof T] - anyOf: TLiteral[] -} -// -------------------------------------------------------------------------- -// TExtends -// -------------------------------------------------------------------------- -// prettier-ignore -export type TExtends = - (Static extends Static ? T : U) extends infer O ? - UnionToTuple extends [infer X, infer Y] ? TUnion<[AssertType, AssertType]> : AssertType - : never -// -------------------------------------------------------------------------- -// TExclude -// -------------------------------------------------------------------------- -export type TExcludeTemplateLiteralResult = UnionType }[T]>>> -export type TExcludeTemplateLiteral = Exclude, Static> extends infer S ? TExcludeTemplateLiteralResult> : never -// prettier-ignore -export type TExcludeArray = AssertRest> extends Static ? never : T[K] -}[number]>> extends infer R ? UnionType> : never -// prettier-ignore -export type TExclude = - T extends TTemplateLiteral ? TExcludeTemplateLiteral : - T extends TUnion ? TExcludeArray : - T extends U ? TNever : T -// -------------------------------------------------------------------------- -// TExtract -// -------------------------------------------------------------------------- -export type TExtractTemplateLiteralResult = UnionType }[T]>>> -export type TExtractTemplateLiteral = Extract, Static> extends infer S ? TExtractTemplateLiteralResult> : never -// prettier-ignore -export type TExtractArray = AssertRest> extends Static ? T[K] : never -}[number]>> extends infer R ? UnionType> : never -// prettier-ignore -export type TExtract = - T extends TTemplateLiteral ? TExtractTemplateLiteral : - T extends TUnion ? TExtractArray : - T extends U ? T : T -// -------------------------------------------------------------------------- -// TFunction -// -------------------------------------------------------------------------- -export type TFunctionReturnTypeResolve = Static -export type TFunctionParametersResolve = T extends [infer L extends TSchema, ...infer R extends TSchema[]] ? [Static, ...TFunctionParametersResolve] : [] -export type TFunctionResolve = Ensure<(...param: TFunctionParametersResolve) => TFunctionReturnTypeResolve> -export interface TFunction extends TSchema { - [Kind]: 'Function' - static: TFunctionResolve - type: 'Function' - parameters: T - returns: U -} -// -------------------------------------------------------------------------- -// TIndex -// -------------------------------------------------------------------------- -export type TIndexRest = T extends [infer L, ...infer R] ? [TIndexType, K>, ...TIndexRest, K>] : [] -export type TIndexProperty = K extends keyof T ? [T[K]] : [] -export type TIndexTuple = K extends keyof T ? [T[K]] : [] -// prettier-ignore -export type TIndexType = - T extends TRecursive ? TIndexType : - T extends TIntersect ? IntersectType>, TNever>>> : - T extends TUnion ? UnionType>>> : - T extends TObject ? UnionType>>> : - T extends TTuple ? UnionType>>> : - [] -// prettier-ignore -export type TIndexRestMany = - K extends [infer L, ...infer R] ? [TIndexType>, ...TIndexRestMany>] : - [] -// prettier-ignore -export type TIndex = - T extends TRecursive ? TIndex : - T extends TIntersect ? UnionType>> : - T extends TUnion ? UnionType>> : - T extends TObject ? UnionType>> : - T extends TTuple ? UnionType>> : - TNever -// -------------------------------------------------------------------------- -// TIntrinsic -// -------------------------------------------------------------------------- -export type TIntrinsicMode = 'Uppercase' | 'Lowercase' | 'Capitalize' | 'Uncapitalize' -// prettier-ignore -export type TIntrinsicTemplateLiteral = - M extends ('Lowercase' | 'Uppercase') ? T extends [infer L, ...infer R] ? [TIntrinsic, M>, ...TIntrinsicTemplateLiteral, M>] : T : - M extends ('Capitalize' | 'Uncapitalize') ? T extends [infer L, ...infer R] ? [TIntrinsic, M>, ...R] : T : - T -// prettier-ignore -export type TIntrinsicLiteral = - T extends string ? - M extends 'Uncapitalize' ? Uncapitalize : - M extends 'Capitalize' ? Capitalize : - M extends 'Uppercase' ? Uppercase : - M extends 'Lowercase' ? Lowercase : - string - : T -// prettier-ignore -export type TIntrinsicRest = T extends [infer L, ...infer R] - ? [TIntrinsic, M>, ...TIntrinsicRest, M>] - : [] -// prettier-ignore -export type TIntrinsic = - T extends TTemplateLiteral ? TTemplateLiteral> : - T extends TUnion ? TUnion> : - T extends TLiteral ? TLiteral> : - T -// -------------------------------------------------------------------------- -// TInteger -// -------------------------------------------------------------------------- -export interface TInteger extends TSchema, NumericOptions { - [Kind]: 'Integer' - static: number - type: 'integer' -} -// -------------------------------------------------------------------------- -// TIntersect -// -------------------------------------------------------------------------- -export type TUnevaluatedProperties = undefined | TSchema | boolean -export interface IntersectOptions extends SchemaOptions { - unevaluatedProperties?: TUnevaluatedProperties -} -export interface TIntersect extends TSchema, IntersectOptions { - [Kind]: 'Intersect' - static: TupleToIntersect<{ [K in keyof T]: Static, this['params']> }> - type?: 'object' - allOf: [...T] -} -// -------------------------------------------------------------------------- -// TIterator -// -------------------------------------------------------------------------- -export interface TIterator extends TSchema { - [Kind]: 'Iterator' - static: IterableIterator> - type: 'Iterator' - items: T -} -// -------------------------------------------------------------------------- -// TKeyOf -// -------------------------------------------------------------------------- -// prettier-ignore -export type TKeyOfProperties = Discard extends infer S - ? UnionToTuple<{[K in keyof S]: TLiteral>}[keyof S]> - : [], undefined> // note: optional properties produce undefined types in tuple result. discard. -// prettier-ignore -export type TKeyOfIndicesArray = UnionToTuple -// prettier-ignore -export type TKeyOfIndices = AssertRest extends infer R ? { - [K in keyof R] : TLiteral> -}: []> -// prettier-ignore -export type TKeyOf = ( - T extends TRecursive ? TKeyOfProperties : - T extends TIntersect ? TKeyOfProperties : - T extends TUnion ? TKeyOfProperties : - T extends TObject ? TKeyOfProperties : - T extends TTuple ? TKeyOfIndices : - T extends TArray ? [TNumber] : - T extends TRecord ? [K] : - [] -) extends infer R ? UnionType> : never -// -------------------------------------------------------------------------- -// TLiteral -// -------------------------------------------------------------------------- -export type TLiteralValue = boolean | number | string // | bigint - supported but variant disable due to potential numeric type conflicts -export type TLiteralBoolean = TLiteral -export type TLiteralNumber = TLiteral -export type TLiteralString = TLiteral -export interface TLiteral extends TSchema { - [Kind]: 'Literal' - static: T - const: T -} -// -------------------------------------------------------------------------- -// TNever -// -------------------------------------------------------------------------- -export interface TNever extends TSchema { - [Kind]: 'Never' - static: never - not: {} -} -// -------------------------------------------------------------------------- -// TNot -// -------------------------------------------------------------------------- -export interface TNot extends TSchema { - [Kind]: 'Not' - static: T extends TNot ? Static : unknown - not: T -} -// -------------------------------------------------------------------------- -// TNull -// -------------------------------------------------------------------------- -export interface TNull extends TSchema { - [Kind]: 'Null' - static: null - type: 'null' -} -// -------------------------------------------------------------------------- -// TNumber -// -------------------------------------------------------------------------- -export interface TNumber extends TSchema, NumericOptions { - [Kind]: 'Number' - static: number - type: 'number' -} -// -------------------------------------------------------------------------- -// TObject -// -------------------------------------------------------------------------- -export type ReadonlyOptionalPropertyKeys = { [K in keyof T]: T[K] extends TReadonly ? (T[K] extends TOptional ? K : never) : never }[keyof T] -export type ReadonlyPropertyKeys = { [K in keyof T]: T[K] extends TReadonly ? (T[K] extends TOptional ? never : K) : never }[keyof T] -export type OptionalPropertyKeys = { [K in keyof T]: T[K] extends TOptional ? (T[K] extends TReadonly ? never : K) : never }[keyof T] -export type RequiredPropertyKeys = keyof Omit | ReadonlyPropertyKeys | OptionalPropertyKeys> -// prettier-ignore -export type PropertiesReducer> = Evaluate<( - Readonly>>> & - Readonly>> & - Partial>> & - Required>> -)> -// prettier-ignore -export type PropertiesReduce = PropertiesReducer -}> -export type TPropertyKey = string | number -export type TProperties = Record -export type ObjectProperties = T extends TObject ? U : never -export type ObjectPropertyKeys = T extends TObject ? keyof U : never -export type TAdditionalProperties = undefined | TSchema | boolean -export interface ObjectOptions extends SchemaOptions { - /** Additional property constraints for this object */ - additionalProperties?: TAdditionalProperties - /** The minimum number of properties allowed on this object */ - minProperties?: number - /** The maximum number of properties allowed on this object */ - maxProperties?: number -} -export interface TObject extends TSchema, ObjectOptions { - [Kind]: 'Object' - static: PropertiesReduce - additionalProperties?: TAdditionalProperties - type: 'object' - properties: T - required?: string[] -} -// -------------------------------------------------------------------------- -// TOmit -// -------------------------------------------------------------------------- -export type TOmitProperties = Evaluate>> -export type TOmitRest = AssertRest<{ [K2 in keyof T]: TOmit, K> }> -// prettier-ignore -export type TOmit = - T extends TRecursive ? TRecursive> : - T extends TIntersect ? TIntersect> : - T extends TUnion ? TUnion> : - T extends TObject ? TObject> : - T -// -------------------------------------------------------------------------- -// TParameters -// -------------------------------------------------------------------------- -export type TParameters = Ensure> -// -------------------------------------------------------------------------- -// TPartial -// -------------------------------------------------------------------------- -export type TPartialObjectArray = AssertRest<{ [K in keyof T]: TPartial> }, TObject[]> -export type TPartialRest = AssertRest<{ [K in keyof T]: TPartial> }> -// prettier-ignore -export type TPartialProperties = Evaluate) ? TReadonlyOptional : - T[K] extends (TReadonly) ? TReadonlyOptional : - T[K] extends (TOptional) ? TOptional : - TOptional -}>> -// prettier-ignore -export type TPartial = - T extends TRecursive ? TRecursive> : - T extends TIntersect ? TIntersect> : - T extends TUnion ? TUnion> : - T extends TObject ? TObject> : - T -// -------------------------------------------------------------------------- -// TPick -// -------------------------------------------------------------------------- -// Note the key K will overlap for varying TProperties gathered via recursive union and intersect traversal. Because of this, -// we need to extract only keys assignable to T on K2. This behavior is only required for Pick only. -// prettier-ignore -export type TPickProperties = - Pick, keyof T>> extends infer R ? ({ - [K in keyof R]: AssertType extends TSchema ? R[K] : never - }): never -export type TPickRest = { [K2 in keyof T]: TPick, K> } -// prettier-ignore -export type TPick = - T extends TRecursive ? TRecursive> : - T extends TIntersect ? TIntersect> : - T extends TUnion ? TUnion> : - T extends TObject ? TObject> : - T -// -------------------------------------------------------------------------- -// TPromise -// -------------------------------------------------------------------------- -export interface TPromise extends TSchema { - [Kind]: 'Promise' - static: Promise> - type: 'Promise' - item: TSchema -} -// -------------------------------------------------------------------------- -// TRecord -// -------------------------------------------------------------------------- -export type TRecordFromUnionLiteralString = { [_ in K['const']]: T } -export type TRecordFromUnionLiteralNumber = { [_ in K['const']]: T } -// prettier-ignore -export type TRecordFromEnumKey, T extends TSchema> = Ensure> -// prettier-ignore -export type TRecordFromUnionRest = K extends [infer L, ...infer R] ? ( - L extends TUnion ? TRecordFromUnionRest & TRecordFromUnionRest, T> : - L extends TLiteralString ? TRecordFromUnionLiteralString & TRecordFromUnionRest, T> : - L extends TLiteralNumber ? TRecordFromUnionLiteralNumber & TRecordFromUnionRest, T> : -{}) : {} -export type TRecordFromUnion = Ensure>>>> -export type TRecordFromTemplateLiteralKeyInfinite = Ensure> -export type TRecordFromTemplateLiteralKeyFinite> = Ensure]: T }>>> -// prettier-ignore -export type TRecordFromTemplateLiteralKey = IsTemplateLiteralFinite extends false - ? TRecordFromTemplateLiteralKeyInfinite - : TRecordFromTemplateLiteralKeyFinite -export type TRecordFromLiteralStringKey = Ensure> -export type TRecordFromLiteralNumberKey = Ensure> -export type TRecordFromStringKey = Ensure> -export type TRecordFromNumberKey = Ensure> -export type TRecordFromIntegerKey = Ensure> -// prettier-ignore -export type TRecordResolve = - K extends TEnum ? TRecordFromEnumKey : // Enum before Union (intercept Hint) - K extends TUnion ? TRecordFromUnion : - K extends TTemplateLiteral ? TRecordFromTemplateLiteralKey : - K extends TLiteralString ? TRecordFromLiteralStringKey : - K extends TLiteralNumber ? TRecordFromLiteralNumberKey : - K extends TString ? TRecordFromStringKey : - K extends TNumber ? TRecordFromNumberKey : - K extends TInteger ? TRecordFromIntegerKey : - TNever -export interface TRecord extends TSchema { - [Kind]: 'Record' - static: Record, string | number>, Static> - type: 'object' - patternProperties: { [pattern: string]: T } - additionalProperties: TAdditionalProperties -} -// -------------------------------------------------------------------------- -// TRecursive -// -------------------------------------------------------------------------- -export interface TThis extends TSchema { - [Kind]: 'This' - static: this['params'][0] - $ref: string -} -export type TRecursiveReduce = Static]> -export interface TRecursive extends TSchema { - [Hint]: 'Recursive' - static: TRecursiveReduce -} -// -------------------------------------------------------------------------- -// TRef -// -------------------------------------------------------------------------- -export interface TRef extends TSchema { - [Kind]: 'Ref' - static: Static - $ref: string -} -// -------------------------------------------------------------------------- -// TRest -// -------------------------------------------------------------------------- -export type TRest = T extends TIntersect ? R : T extends TUnion ? R : T extends TTuple ? R : [] -// -------------------------------------------------------------------------- -// TReturnType -// -------------------------------------------------------------------------- -export type TReturnType = T['returns'] -// -------------------------------------------------------------------------- -// TRequired -// -------------------------------------------------------------------------- -export type TRequiredRest = AssertRest<{ [K in keyof T]: TRequired> }> -// prettier-ignore -export type TRequiredProperties = Evaluate) ? TReadonly : - T[K] extends (TReadonly) ? TReadonly : - T[K] extends (TOptional) ? S : - T[K] -}>> -// prettier-ignore -export type TRequired = - T extends TRecursive ? TRecursive> : - T extends TIntersect ? TIntersect> : - T extends TUnion ? TUnion> : - T extends TObject ? TObject> : - T -// -------------------------------------------------------------------------- -// TString -// -------------------------------------------------------------------------- -export type StringFormatOption = - | 'date-time' - | 'time' - | 'date' - | 'email' - | 'idn-email' - | 'hostname' - | 'idn-hostname' - | 'ipv4' - | 'ipv6' - | 'uri' - | 'uri-reference' - | 'iri' - | 'uuid' - | 'iri-reference' - | 'uri-template' - | 'json-pointer' - | 'relative-json-pointer' - | 'regex' - | ({} & string) -// prettier-ignore -export type StringContentEncodingOption = - | '7bit' - | '8bit' - | 'binary' - | 'quoted-printable' - | 'base64' - | ({} & string) -export interface StringOptions extends SchemaOptions { - /** The maximum string length */ - maxLength?: number - /** The minimum string length */ - minLength?: number - /** A regular expression pattern this string should match */ - pattern?: string - /** A format this string should match */ - format?: StringFormatOption - /** The content encoding for this string */ - contentEncoding?: StringContentEncodingOption - /** The content media type for this string */ - contentMediaType?: string -} -export interface TString extends TSchema, StringOptions { - [Kind]: 'String' - static: string - type: 'string' -} -// -------------------------------------------------------------------------- -// TSymbol -// -------------------------------------------------------------------------- -export type SymbolValue = string | number | undefined -export interface TSymbol extends TSchema, SchemaOptions { - [Kind]: 'Symbol' - static: symbol - type: 'symbol' -} -// ------------------------------------------------------------------------- -// TTemplateLiteralParserDsl -// ------------------------------------------------------------------------- -// prettier-ignore -export type TTemplateLiteralDslParserUnionLiteral = - T extends `${infer L}|${infer R}` ? [TLiteral>, ...TTemplateLiteralDslParserUnionLiteral] : - T extends `${infer L}` ? [TLiteral>] : - [] -export type TTemplateLiteralDslParserUnion = UnionType> -// prettier-ignore -export type TTemplateLiteralDslParserTerminal = - T extends 'boolean' ? TBoolean : - T extends 'bigint' ? TBigInt : - T extends 'number' ? TNumber : - T extends 'string' ? TString : - TTemplateLiteralDslParserUnion -// prettier-ignore -export type TTemplateLiteralDslParserTemplate = - T extends `{${infer L}}${infer R}` ? [TTemplateLiteralDslParserTerminal, ...TTemplateLiteralDslParserTemplate] : - T extends `${infer L}$${infer R}` ? [TLiteral, ...TTemplateLiteralDslParserTemplate] : - T extends `${infer L}` ? [TLiteral] : - [] -export type TTemplateLiteralDslParser = Ensure, TTemplateLiteralKind[]>>> -// -------------------------------------------------------------------------- -// TTemplateLiteral -// -------------------------------------------------------------------------- -// prettier-ignore -export type IsTemplateLiteralFiniteCheck = - T extends TTemplateLiteral ? IsTemplateLiteralFiniteArray> : - T extends TUnion ? IsTemplateLiteralFiniteArray> : - T extends TString ? false : - T extends TBoolean ? false : - T extends TNumber ? false : - T extends TInteger ? false : - T extends TBigInt ? false : - T extends TLiteral ? true : - false -// prettier-ignore -export type IsTemplateLiteralFiniteArray = - T extends [infer L, ...infer R] ? IsTemplateLiteralFiniteCheck extends false ? false : IsTemplateLiteralFiniteArray> : - true -export type IsTemplateLiteralFinite = T extends TTemplateLiteral ? IsTemplateLiteralFiniteArray : false -export type TTemplateLiteralKind = TUnion | TLiteral | TInteger | TTemplateLiteral | TNumber | TBigInt | TString | TBoolean | TNever -// prettier-ignore -export type TTemplateLiteralConst = - T extends TUnion ? { [K in keyof U]: TTemplateLiteralUnion, Acc> }[number] : - T extends TTemplateLiteral ? `${Static}` : - T extends TLiteral ? `${U}` : - T extends TString ? `${string}` : - T extends TNumber ? `${number}` : - T extends TBigInt ? `${bigint}` : - T extends TBoolean ? `${boolean}` : - never -// prettier-ignore -export type TTemplateLiteralUnion = - T extends [infer L, ...infer R] ? `${TTemplateLiteralConst}${TTemplateLiteralUnion, Acc>}` : - Acc -export type TTemplateLiteralKeyRest = Assert>, TPropertyKey[]> -export interface TTemplateLiteral extends TSchema { - [Kind]: 'TemplateLiteral' - static: TTemplateLiteralUnion - type: 'string' - pattern: string // todo: it may be possible to infer this pattern -} -// -------------------------------------------------------------------------- -// TTransform -// -------------------------------------------------------------------------- -// prettier-ignore -export type DecodeProperties = { - [K in keyof T]: DecodeType -} -// prettier-ignore -export type DecodeRest = T extends [infer L extends TSchema, ...infer R extends TSchema[]] - ? [DecodeType, ...DecodeRest] - : [] -// prettier-ignore -export type DecodeType = ( - T extends TOptional ? TOptional> : - T extends TReadonly ? TReadonly> : - T extends TTransform ? TUnsafe : - T extends TArray ? TArray> : - T extends TAsyncIterator ? TAsyncIterator> : - T extends TConstructor ? TConstructor, DecodeType> : - T extends TEnum ? TEnum : // intercept for union. interior non decodable - T extends TFunction ? TFunction, DecodeType> : - T extends TIntersect ? TIntersect> : - T extends TIterator ? TIterator> : - T extends TNot ? TNot> : - T extends TObject ? TObject>> : - T extends TPromise ? TPromise> : - T extends TRecord ? TRecord> : - T extends TRecursive ? TRecursive> : - T extends TRef ? TRef> : - T extends TTuple ? TTuple> : - T extends TUnion ? TUnion> : - T -) -export type TransformFunction = (value: T) => U -export interface TransformOptions { - Decode: TransformFunction, O> - Encode: TransformFunction> -} -export type TTransformResolve = T extends TTransform ? S : Static -export interface TTransform extends TSchema { - static: TTransformResolve - [Transform]: TransformOptions - [key: string]: any -} -// -------------------------------------------------------------------------- -// TTuple -// -------------------------------------------------------------------------- -export type TTupleRest = T extends [infer L, ...infer R] ? [Static, P>, ...TTupleRest, P>] : [] -export interface TTuple extends TSchema { - [Kind]: 'Tuple' - static: TTupleRest - type: 'array' - items?: T - additionalItems?: false - minItems: number - maxItems: number -} -// -------------------------------------------------------------------------- -// TUndefined -// -------------------------------------------------------------------------- -export interface TUndefined extends TSchema { - [Kind]: 'Undefined' - static: undefined - type: 'undefined' -} -// -------------------------------------------------------------------------- -// TUnionLiteral -// -------------------------------------------------------------------------- -// prettier-ignore -export type TLiteralUnionReduce[]> = - T extends [infer L, ...infer R] ? [Assert>['const'], ...TLiteralUnionReduce[]>>] : - [] -// prettier-ignore -export type TUnionLiteralKeyRest[]>> = - T extends TUnion ? TLiteralUnionReduce[]>> : - [] -// -------------------------------------------------------------------------- -// TUnion -// -------------------------------------------------------------------------- -// prettier-ignore -export type TUnionTemplateLiteral> = Ensure}[S]>,TLiteral[]>>> -export interface TUnion extends TSchema { - [Kind]: 'Union' - static: { [K in keyof T]: T[K] extends TSchema ? Static : never }[number] - anyOf: T -} -// -------------------------------------------------------------------------- -// TUint8Array -// -------------------------------------------------------------------------- -export interface Uint8ArrayOptions extends SchemaOptions { - maxByteLength?: number - minByteLength?: number -} -export interface TUint8Array extends TSchema, Uint8ArrayOptions { - [Kind]: 'Uint8Array' - static: Uint8Array - type: 'uint8array' -} -// -------------------------------------------------------------------------- -// TUnknown -// -------------------------------------------------------------------------- -export interface TUnknown extends TSchema { - [Kind]: 'Unknown' - static: unknown -} -// -------------------------------------------------------------------------- -// TUnsafe -// -------------------------------------------------------------------------- -export interface UnsafeOptions extends SchemaOptions { - [Kind]?: string -} -export interface TUnsafe extends TSchema { - [Kind]: string - static: T -} -// -------------------------------------------------------------------------- -// TVoid -// -------------------------------------------------------------------------- -export interface TVoid extends TSchema { - [Kind]: 'Void' - static: void - type: 'void' -} -// -------------------------------------------------------------------------- -// Static -// -------------------------------------------------------------------------- -/** Creates an decoded static type from a TypeBox type */ -export type StaticDecode = Static, P> -/** Creates an encoded static type from a TypeBox type */ -export type StaticEncode = Static -/** Creates a static type from a TypeBox type */ -export type Static = (T & { params: P })['static'] -// -------------------------------------------------------------------------- -// TypeRegistry -// -------------------------------------------------------------------------- -export type TypeRegistryValidationFunction = (schema: TSchema, value: unknown) => boolean -/** A registry for user defined types */ -export namespace TypeRegistry { - const map = new Map>() - /** Returns the entries in this registry */ - export function Entries() { - return new Map(map) - } - /** Clears all user defined types */ - export function Clear() { - return map.clear() - } - /** Deletes a registered type */ - export function Delete(kind: string) { - return map.delete(kind) - } - /** Returns true if this registry contains this kind */ - export function Has(kind: string) { - return map.has(kind) - } - /** Sets a validation function for a user defined type */ - export function Set(kind: string, func: TypeRegistryValidationFunction) { - map.set(kind, func) - } - /** Gets a custom validation function for a user defined type */ - export function Get(kind: string) { - return map.get(kind) - } -} -// -------------------------------------------------------------------------- -// TypeBoxError -// -------------------------------------------------------------------------- -export class TypeBoxError extends Error { - constructor(message: string) { - super(message) - } -} -// -------------------------------------------------------------------------- -// TypeRegistry -// -------------------------------------------------------------------------- -export type FormatRegistryValidationFunction = (value: string) => boolean -/** A registry for user defined string formats */ -export namespace FormatRegistry { - const map = new Map() - /** Returns the entries in this registry */ - export function Entries() { - return new Map(map) - } - /** Clears all user defined string formats */ - export function Clear() { - return map.clear() - } - /** Deletes a registered format */ - export function Delete(format: string) { - return map.delete(format) - } - /** Returns true if the user defined string format exists */ - export function Has(format: string) { - return map.has(format) - } - /** Sets a validation function for a user defined string format */ - export function Set(format: string, func: FormatRegistryValidationFunction) { - map.set(format, func) - } - /** Gets a validation function for a user defined string format */ - export function Get(format: string) { - return map.get(format) - } -} -// -------------------------------------------------------------------------- -// ValueGuard -// -------------------------------------------------------------------------- -/** Provides functions to type guard raw JavaScript values */ -export namespace ValueGuard { - /** Returns true if this value is an array */ - export function IsArray(value: unknown): value is unknown[] { - return Array.isArray(value) - } - /** Returns true if this value is bigint */ - export function IsBigInt(value: unknown): value is bigint { - return typeof value === 'bigint' - } - /** Returns true if this value is a boolean */ - export function IsBoolean(value: unknown): value is boolean { - return typeof value === 'boolean' - } - /** Returns true if this value is a Date object */ - export function IsDate(value: unknown): value is Date { - return value instanceof globalThis.Date - } - /** Returns true if this value is null */ - export function IsNull(value: unknown): value is null { - return value === null - } - /** Returns true if this value is number */ - export function IsNumber(value: unknown): value is number { - return typeof value === 'number' - } - /** Returns true if this value is an object */ - export function IsObject(value: unknown): value is Record { - return typeof value === 'object' && value !== null - } - /** Returns true if this value is string */ - export function IsString(value: unknown): value is string { - return typeof value === 'string' - } - /** Returns true if this value is a Uint8Array */ - export function IsUint8Array(value: unknown): value is Uint8Array { - return value instanceof globalThis.Uint8Array - } - /** Returns true if this value is undefined */ - export function IsUndefined(value: unknown): value is undefined { - return value === undefined - } -} -// -------------------------------------------------------------------------- -// TypeGuard -// -------------------------------------------------------------------------- -export class TypeGuardUnknownTypeError extends TypeBoxError {} -/** Provides functions to test if JavaScript values are TypeBox types */ -export namespace TypeGuard { - function IsPattern(value: unknown): value is string { - try { - new RegExp(value as string) - return true - } catch { - return false - } - } - function IsControlCharacterFree(value: unknown): value is string { - if (!ValueGuard.IsString(value)) return false - for (let i = 0; i < value.length; i++) { - const code = value.charCodeAt(i) - if ((code >= 7 && code <= 13) || code === 27 || code === 127) { - return false - } - } - return true - } - function IsAdditionalProperties(value: unknown): value is TAdditionalProperties { - return IsOptionalBoolean(value) || TSchema(value) - } - function IsOptionalBigInt(value: unknown): value is bigint | undefined { - return ValueGuard.IsUndefined(value) || ValueGuard.IsBigInt(value) - } - function IsOptionalNumber(value: unknown): value is number | undefined { - return ValueGuard.IsUndefined(value) || ValueGuard.IsNumber(value) - } - function IsOptionalBoolean(value: unknown): value is boolean | undefined { - return ValueGuard.IsUndefined(value) || ValueGuard.IsBoolean(value) - } - function IsOptionalString(value: unknown): value is string | undefined { - return ValueGuard.IsUndefined(value) || ValueGuard.IsString(value) - } - function IsOptionalPattern(value: unknown): value is string | undefined { - return ValueGuard.IsUndefined(value) || (ValueGuard.IsString(value) && IsControlCharacterFree(value) && IsPattern(value)) - } - function IsOptionalFormat(value: unknown): value is string | undefined { - return ValueGuard.IsUndefined(value) || (ValueGuard.IsString(value) && IsControlCharacterFree(value)) - } - function IsOptionalSchema(value: unknown): value is boolean | undefined { - return ValueGuard.IsUndefined(value) || TSchema(value) - } - // ---------------------------------------------------------------- - // Types - // ---------------------------------------------------------------- - /** Returns true if the given value is TAny */ - export function TAny(schema: unknown): schema is TAny { - // prettier-ignore - return ( - TKindOf(schema, 'Any') && - IsOptionalString(schema.$id) - ) - } - /** Returns true if the given value is TArray */ - export function TArray(schema: unknown): schema is TArray { - return ( - TKindOf(schema, 'Array') && - schema.type === 'array' && - IsOptionalString(schema.$id) && - TSchema(schema.items) && - IsOptionalNumber(schema.minItems) && - IsOptionalNumber(schema.maxItems) && - IsOptionalBoolean(schema.uniqueItems) && - IsOptionalSchema(schema.contains) && - IsOptionalNumber(schema.minContains) && - IsOptionalNumber(schema.maxContains) - ) - } - /** Returns true if the given value is TAsyncIterator */ - export function TAsyncIterator(schema: unknown): schema is TAsyncIterator { - // prettier-ignore - return ( - TKindOf(schema, 'AsyncIterator') && - schema.type === 'AsyncIterator' && - IsOptionalString(schema.$id) && - TSchema(schema.items) - ) - } - /** Returns true if the given value is TBigInt */ - export function TBigInt(schema: unknown): schema is TBigInt { - // prettier-ignore - return ( - TKindOf(schema, 'BigInt') && - schema.type === 'bigint' && - IsOptionalString(schema.$id) && - IsOptionalBigInt(schema.exclusiveMaximum) && - IsOptionalBigInt(schema.exclusiveMinimum) && - IsOptionalBigInt(schema.maximum) && - IsOptionalBigInt(schema.minimum) && - IsOptionalBigInt(schema.multipleOf) - ) - } - /** Returns true if the given value is TBoolean */ - export function TBoolean(schema: unknown): schema is TBoolean { - // prettier-ignore - return ( - TKindOf(schema, 'Boolean') && - schema.type === 'boolean' && - IsOptionalString(schema.$id) - ) - } - /** Returns true if the given value is TConstructor */ - export function TConstructor(schema: unknown): schema is TConstructor { - // prettier-ignore - return ( - TKindOf(schema, 'Constructor') && - schema.type === 'Constructor' && - IsOptionalString(schema.$id) && - ValueGuard.IsArray(schema.parameters) && - schema.parameters.every(schema => TSchema(schema)) && - TSchema(schema.returns) - ) - } - /** Returns true if the given value is TDate */ - export function TDate(schema: unknown): schema is TDate { - return ( - TKindOf(schema, 'Date') && - schema.type === 'Date' && - IsOptionalString(schema.$id) && - IsOptionalNumber(schema.exclusiveMaximumTimestamp) && - IsOptionalNumber(schema.exclusiveMinimumTimestamp) && - IsOptionalNumber(schema.maximumTimestamp) && - IsOptionalNumber(schema.minimumTimestamp) && - IsOptionalNumber(schema.multipleOfTimestamp) - ) - } - /** Returns true if the given value is TFunction */ - export function TFunction(schema: unknown): schema is TFunction { - // prettier-ignore - return ( - TKindOf(schema, 'Function') && - schema.type === 'Function' && - IsOptionalString(schema.$id) && - ValueGuard.IsArray(schema.parameters) && - schema.parameters.every(schema => TSchema(schema)) && - TSchema(schema.returns) - ) - } - /** Returns true if the given value is TInteger */ - export function TInteger(schema: unknown): schema is TInteger { - return ( - TKindOf(schema, 'Integer') && - schema.type === 'integer' && - IsOptionalString(schema.$id) && - IsOptionalNumber(schema.exclusiveMaximum) && - IsOptionalNumber(schema.exclusiveMinimum) && - IsOptionalNumber(schema.maximum) && - IsOptionalNumber(schema.minimum) && - IsOptionalNumber(schema.multipleOf) - ) - } - /** Returns true if the given value is TIntersect */ - export function TIntersect(schema: unknown): schema is TIntersect { - // prettier-ignore - return ( - TKindOf(schema, 'Intersect') && - (ValueGuard.IsString(schema.type) && schema.type !== 'object' ? false : true) && - ValueGuard.IsArray(schema.allOf) && - schema.allOf.every(schema => TSchema(schema) && !TTransform(schema)) && - IsOptionalString(schema.type) && - (IsOptionalBoolean(schema.unevaluatedProperties) || IsOptionalSchema(schema.unevaluatedProperties)) && - IsOptionalString(schema.$id) - ) - } - /** Returns true if the given value is TIterator */ - export function TIterator(schema: unknown): schema is TIterator { - // prettier-ignore - return ( - TKindOf(schema, 'Iterator') && - schema.type === 'Iterator' && - IsOptionalString(schema.$id) && - TSchema(schema.items) - ) - } - /** Returns true if the given value is a TKind with the given name. */ - export function TKindOf(schema: unknown, kind: T): schema is Record & { [Kind]: T } { - return TKind(schema) && schema[Kind] === kind - } - /** Returns true if the given value is TKind */ - export function TKind(schema: unknown): schema is Record & { [Kind]: string } { - return ValueGuard.IsObject(schema) && Kind in schema && ValueGuard.IsString(schema[Kind]) - } - /** Returns true if the given value is TLiteral */ - export function TLiteralString(schema: unknown): schema is TLiteral { - return TLiteral(schema) && ValueGuard.IsString(schema.const) - } - /** Returns true if the given value is TLiteral */ - export function TLiteralNumber(schema: unknown): schema is TLiteral { - return TLiteral(schema) && ValueGuard.IsNumber(schema.const) - } - /** Returns true if the given value is TLiteral */ - export function TLiteralBoolean(schema: unknown): schema is TLiteral { - return TLiteral(schema) && ValueGuard.IsBoolean(schema.const) - } - /** Returns true if the given value is TLiteral */ - export function TLiteral(schema: unknown): schema is TLiteral { - // prettier-ignore - return ( - TKindOf(schema, 'Literal') && - IsOptionalString(schema.$id) && ( - ValueGuard.IsBoolean(schema.const) || - ValueGuard.IsNumber(schema.const) || - ValueGuard.IsString(schema.const) - ) - ) - } - /** Returns true if the given value is TNever */ - export function TNever(schema: unknown): schema is TNever { - // prettier-ignore - return ( - TKindOf(schema, 'Never') && - ValueGuard.IsObject(schema.not) && - Object.getOwnPropertyNames(schema.not).length === 0 - ) - } - /** Returns true if the given value is TNot */ - export function TNot(schema: unknown): schema is TNot { - // prettier-ignore - return ( - TKindOf(schema, 'Not') && - TSchema(schema.not) - ) - } - /** Returns true if the given value is TNull */ - export function TNull(schema: unknown): schema is TNull { - // prettier-ignore - return ( - TKindOf(schema, 'Null') && - schema.type === 'null' && - IsOptionalString(schema.$id) - ) - } - /** Returns true if the given value is TNumber */ - export function TNumber(schema: unknown): schema is TNumber { - return ( - TKindOf(schema, 'Number') && - schema.type === 'number' && - IsOptionalString(schema.$id) && - IsOptionalNumber(schema.exclusiveMaximum) && - IsOptionalNumber(schema.exclusiveMinimum) && - IsOptionalNumber(schema.maximum) && - IsOptionalNumber(schema.minimum) && - IsOptionalNumber(schema.multipleOf) - ) - } - /** Returns true if the given value is TObject */ - export function TObject(schema: unknown): schema is TObject { - // prettier-ignore - return ( - TKindOf(schema, 'Object') && - schema.type === 'object' && - IsOptionalString(schema.$id) && - ValueGuard.IsObject(schema.properties) && - IsAdditionalProperties(schema.additionalProperties) && - IsOptionalNumber(schema.minProperties) && - IsOptionalNumber(schema.maxProperties) && - Object.entries(schema.properties).every(([key, schema]) => IsControlCharacterFree(key) && TSchema(schema)) - ) - } - /** Returns true if the given value is TPromise */ - export function TPromise(schema: unknown): schema is TPromise { - // prettier-ignore - return ( - TKindOf(schema, 'Promise') && - schema.type === 'Promise' && - IsOptionalString(schema.$id) && - TSchema(schema.item) - ) - } - /** Returns true if the given value is TRecord */ - export function TRecord(schema: unknown): schema is TRecord { - // prettier-ignore - return ( - TKindOf(schema, 'Record') && - schema.type === 'object' && - IsOptionalString(schema.$id) && - IsAdditionalProperties(schema.additionalProperties) && - ValueGuard.IsObject(schema.patternProperties) && - ((schema: Record) => { - const keys = Object.getOwnPropertyNames(schema.patternProperties) - return ( - keys.length === 1 && - IsPattern(keys[0]) && - ValueGuard.IsObject(schema.patternProperties) && - TSchema(schema.patternProperties[keys[0]]) - ) - })(schema) - ) - } - /** Returns true if this value is TRecursive */ - export function TRecursive(schema: unknown): schema is { [Hint]: 'Recursive' } { - return ValueGuard.IsObject(schema) && Hint in schema && schema[Hint] === 'Recursive' - } - /** Returns true if the given value is TRef */ - export function TRef(schema: unknown): schema is TRef { - // prettier-ignore - return ( - TKindOf(schema, 'Ref') && - IsOptionalString(schema.$id) && - ValueGuard.IsString(schema.$ref) - ) - } - /** Returns true if the given value is TString */ - export function TString(schema: unknown): schema is TString { - // prettier-ignore - return ( - TKindOf(schema, 'String') && - schema.type === 'string' && - IsOptionalString(schema.$id) && - IsOptionalNumber(schema.minLength) && - IsOptionalNumber(schema.maxLength) && - IsOptionalPattern(schema.pattern) && - IsOptionalFormat(schema.format) - ) - } - /** Returns true if the given value is TSymbol */ - export function TSymbol(schema: unknown): schema is TSymbol { - // prettier-ignore - return ( - TKindOf(schema, 'Symbol') && - schema.type === 'symbol' && - IsOptionalString(schema.$id) - ) - } - /** Returns true if the given value is TTemplateLiteral */ - export function TTemplateLiteral(schema: unknown): schema is TTemplateLiteral { - // prettier-ignore - return ( - TKindOf(schema, 'TemplateLiteral') && - schema.type === 'string' && - ValueGuard.IsString(schema.pattern) && - schema.pattern[0] === '^' && - schema.pattern[schema.pattern.length - 1] === '$' - ) - } - /** Returns true if the given value is TThis */ - export function TThis(schema: unknown): schema is TThis { - // prettier-ignore - return ( - TKindOf(schema, 'This') && - IsOptionalString(schema.$id) && - ValueGuard.IsString(schema.$ref) - ) - } - /** Returns true of this value is TTransform */ - export function TTransform(schema: unknown): schema is { [Transform]: TransformOptions } { - return ValueGuard.IsObject(schema) && Transform in schema - } - /** Returns true if the given value is TTuple */ - export function TTuple(schema: unknown): schema is TTuple { - // prettier-ignore - return ( - TKindOf(schema, 'Tuple') && - schema.type === 'array' && - IsOptionalString(schema.$id) && - ValueGuard.IsNumber(schema.minItems) && - ValueGuard.IsNumber(schema.maxItems) && - schema.minItems === schema.maxItems && - (( // empty - ValueGuard.IsUndefined(schema.items) && - ValueGuard.IsUndefined(schema.additionalItems) && - schema.minItems === 0 - ) || ( - ValueGuard.IsArray(schema.items) && - schema.items.every(schema => TSchema(schema)) - )) - ) - } - /** Returns true if the given value is TUndefined */ - export function TUndefined(schema: unknown): schema is TUndefined { - // prettier-ignore - return ( - TKindOf(schema, 'Undefined') && - schema.type === 'undefined' && - IsOptionalString(schema.$id) - ) - } - /** Returns true if the given value is TUnion[]> */ - export function TUnionLiteral(schema: unknown): schema is TUnion { - return TUnion(schema) && schema.anyOf.every((schema) => TLiteralString(schema) || TLiteralNumber(schema)) - } - /** Returns true if the given value is TUnion */ - export function TUnion(schema: unknown): schema is TUnion { - // prettier-ignore - return ( - TKindOf(schema, 'Union') && - IsOptionalString(schema.$id) && - ValueGuard.IsObject(schema) && - ValueGuard.IsArray(schema.anyOf) && - schema.anyOf.every(schema => TSchema(schema)) - ) - } - /** Returns true if the given value is TUint8Array */ - export function TUint8Array(schema: unknown): schema is TUint8Array { - // prettier-ignore - return ( - TKindOf(schema, 'Uint8Array') && - schema.type === 'Uint8Array' && - IsOptionalString(schema.$id) && - IsOptionalNumber(schema.minByteLength) && - IsOptionalNumber(schema.maxByteLength) - ) - } - /** Returns true if the given value is TUnknown */ - export function TUnknown(schema: unknown): schema is TUnknown { - // prettier-ignore - return ( - TKindOf(schema, 'Unknown') && - IsOptionalString(schema.$id) - ) - } - /** Returns true if the given value is a raw TUnsafe */ - export function TUnsafe(schema: unknown): schema is TUnsafe { - return TKindOf(schema, 'Unsafe') - } - /** Returns true if the given value is TVoid */ - export function TVoid(schema: unknown): schema is TVoid { - // prettier-ignore - return ( - TKindOf(schema, 'Void') && - schema.type === 'void' && - IsOptionalString(schema.$id) - ) - } - /** Returns true if this value has a Readonly symbol */ - export function TReadonly(schema: T): schema is TReadonly { - return ValueGuard.IsObject(schema) && schema[Readonly] === 'Readonly' - } - /** Returns true if this value has a Optional symbol */ - export function TOptional(schema: T): schema is TOptional { - return ValueGuard.IsObject(schema) && schema[Optional] === 'Optional' - } - /** Returns true if the given value is TSchema */ - export function TSchema(schema: unknown): schema is TSchema { - // prettier-ignore - return ( - ValueGuard.IsObject(schema) - ) && ( - TAny(schema) || - TArray(schema) || - TBoolean(schema) || - TBigInt(schema) || - TAsyncIterator(schema) || - TConstructor(schema) || - TDate(schema) || - TFunction(schema) || - TInteger(schema) || - TIntersect(schema) || - TIterator(schema) || - TLiteral(schema) || - TNever(schema) || - TNot(schema) || - TNull(schema) || - TNumber(schema) || - TObject(schema) || - TPromise(schema) || - TRecord(schema) || - TRef(schema) || - TString(schema) || - TSymbol(schema) || - TTemplateLiteral(schema) || - TThis(schema) || - TTuple(schema) || - TUndefined(schema) || - TUnion(schema) || - TUint8Array(schema) || - TUnknown(schema) || - TUnsafe(schema) || - TVoid(schema) || - (TKind(schema) && TypeRegistry.Has(schema[Kind] as any)) - ) - } -} -// -------------------------------------------------------------------------- -// ExtendsUndefined -// -------------------------------------------------------------------------- -/** Fast undefined check used for properties of type undefined */ -export namespace ExtendsUndefined { - export function Check(schema: TSchema): boolean { - return schema[Kind] === 'Intersect' - ? (schema as TIntersect).allOf.every((schema) => Check(schema)) - : schema[Kind] === 'Union' - ? (schema as TUnion).anyOf.some((schema) => Check(schema)) - : schema[Kind] === 'Undefined' - ? true - : schema[Kind] === 'Not' - ? !Check(schema.not) - : false - } -} -// -------------------------------------------------------------------------- -// TypeExtends -// -------------------------------------------------------------------------- -export class TypeExtendsError extends TypeBoxError {} -export enum TypeExtendsResult { - Union, - True, - False, -} -export namespace TypeExtends { - // -------------------------------------------------------------------------- - // IntoBooleanResult - // -------------------------------------------------------------------------- - function IntoBooleanResult(result: TypeExtendsResult) { - return result === TypeExtendsResult.False ? result : TypeExtendsResult.True - } - // -------------------------------------------------------------------------- - // Throw - // -------------------------------------------------------------------------- - function Throw(message: string): never { - throw new TypeExtendsError(message) - } - // -------------------------------------------------------------------------- - // StructuralRight - // -------------------------------------------------------------------------- - function IsStructuralRight(right: TSchema): boolean { - // prettier-ignore - return ( - TypeGuard.TNever(right) || - TypeGuard.TIntersect(right) || - TypeGuard.TUnion(right) || - TypeGuard.TUnknown(right) || - TypeGuard.TAny(right) - ) - } - function StructuralRight(left: TSchema, right: TSchema) { - // prettier-ignore - return ( - TypeGuard.TNever(right) ? TNeverRight(left, right) : - TypeGuard.TIntersect(right) ? TIntersectRight(left, right) : - TypeGuard.TUnion(right) ? TUnionRight(left, right) : - TypeGuard.TUnknown(right) ? TUnknownRight(left, right) : - TypeGuard.TAny(right) ? TAnyRight(left, right) : - Throw('StructuralRight') - ) - } - // -------------------------------------------------------------------------- - // Any - // -------------------------------------------------------------------------- - function TAnyRight(left: TSchema, right: TAny) { - return TypeExtendsResult.True - } - function TAny(left: TAny, right: TSchema) { - // prettier-ignore - return ( - TypeGuard.TIntersect(right) ? TIntersectRight(left, right) : - (TypeGuard.TUnion(right) && right.anyOf.some((schema) => TypeGuard.TAny(schema) || TypeGuard.TUnknown(schema))) ? TypeExtendsResult.True : - TypeGuard.TUnion(right) ? TypeExtendsResult.Union : - TypeGuard.TUnknown(right) ? TypeExtendsResult.True : - TypeGuard.TAny(right) ? TypeExtendsResult.True : - TypeExtendsResult.Union - ) - } - // -------------------------------------------------------------------------- - // Array - // -------------------------------------------------------------------------- - function TArrayRight(left: TSchema, right: TArray) { - // prettier-ignore - return ( - TypeGuard.TUnknown(left) ? TypeExtendsResult.False : - TypeGuard.TAny(left) ?TypeExtendsResult.Union : - TypeGuard.TNever(left) ? TypeExtendsResult.True : - TypeExtendsResult.False - ) - } - function TArray(left: TArray, right: TSchema) { - // prettier-ignore - return ( - TypeGuard.TObject(right) && IsObjectArrayLike(right) ? TypeExtendsResult.True : - IsStructuralRight(right) ? StructuralRight(left, right) : - !TypeGuard.TArray(right) ? TypeExtendsResult.False : - IntoBooleanResult(Visit(left.items, right.items)) - ) - } - // -------------------------------------------------------------------------- - // AsyncIterator - // -------------------------------------------------------------------------- - function TAsyncIterator(left: TAsyncIterator, right: TSchema) { - // prettier-ignore - return ( - IsStructuralRight(right) ? StructuralRight(left, right) : - !TypeGuard.TAsyncIterator(right) ? TypeExtendsResult.False : - IntoBooleanResult(Visit(left.items, right.items)) - ) - } - // -------------------------------------------------------------------------- - // BigInt - // -------------------------------------------------------------------------- - function TBigInt(left: TBigInt, right: TSchema): TypeExtendsResult { - // prettier-ignore - return ( - IsStructuralRight(right) ? StructuralRight(left, right) : - TypeGuard.TObject(right) ? TObjectRight(left, right) : - TypeGuard.TRecord(right) ? TRecordRight(left, right) : - TypeGuard.TBigInt(right) ? TypeExtendsResult.True : - TypeExtendsResult.False - ) - } - // -------------------------------------------------------------------------- - // Boolean - // -------------------------------------------------------------------------- - function TBooleanRight(left: TSchema, right: TBoolean) { - return TypeGuard.TLiteral(left) && ValueGuard.IsBoolean(left.const) ? TypeExtendsResult.True : TypeGuard.TBoolean(left) ? TypeExtendsResult.True : TypeExtendsResult.False - } - function TBoolean(left: TBoolean, right: TSchema): TypeExtendsResult { - // prettier-ignore - return ( - IsStructuralRight(right) ? StructuralRight(left, right) : - TypeGuard.TObject(right) ? TObjectRight(left, right) : - TypeGuard.TRecord(right) ? TRecordRight(left, right) : - TypeGuard.TBoolean(right) ? TypeExtendsResult.True : - TypeExtendsResult.False - ) - } - // -------------------------------------------------------------------------- - // Constructor - // -------------------------------------------------------------------------- - function TConstructor(left: TConstructor, right: TSchema) { - // prettier-ignore - return ( - IsStructuralRight(right) ? StructuralRight(left, right) : - TypeGuard.TObject(right) ? TObjectRight(left, right) : - !TypeGuard.TConstructor(right) ? TypeExtendsResult.False : - left.parameters.length > right.parameters.length ? TypeExtendsResult.False : - (!left.parameters.every((schema, index) => IntoBooleanResult(Visit(right.parameters[index], schema)) === TypeExtendsResult.True)) ? TypeExtendsResult.False : - IntoBooleanResult(Visit(left.returns, right.returns)) - ) - } - // -------------------------------------------------------------------------- - // Date - // -------------------------------------------------------------------------- - function TDate(left: TDate, right: TSchema) { - // prettier-ignore - return ( - IsStructuralRight(right) ? StructuralRight(left, right) : - TypeGuard.TObject(right) ? TObjectRight(left, right) : - TypeGuard.TRecord(right) ? TRecordRight(left, right) : - TypeGuard.TDate(right) ? TypeExtendsResult.True : - TypeExtendsResult.False - ) - } - // -------------------------------------------------------------------------- - // Function - // -------------------------------------------------------------------------- - function TFunction(left: TFunction, right: TSchema) { - // prettier-ignore - return ( - IsStructuralRight(right) ? StructuralRight(left, right) : - TypeGuard.TObject(right) ? TObjectRight(left, right) : - !TypeGuard.TFunction(right) ? TypeExtendsResult.False : - left.parameters.length > right.parameters.length ? TypeExtendsResult.False : - (!left.parameters.every((schema, index) => IntoBooleanResult(Visit(right.parameters[index], schema)) === TypeExtendsResult.True)) ? TypeExtendsResult.False : - IntoBooleanResult(Visit(left.returns, right.returns)) - ) - } - // -------------------------------------------------------------------------- - // Integer - // -------------------------------------------------------------------------- - function TIntegerRight(left: TSchema, right: TInteger) { - // prettier-ignore - return ( - TypeGuard.TLiteral(left) && ValueGuard.IsNumber(left.const) ? TypeExtendsResult.True : - TypeGuard.TNumber(left) || TypeGuard.TInteger(left) ? TypeExtendsResult.True : - TypeExtendsResult.False - ) - } - function TInteger(left: TInteger, right: TSchema): TypeExtendsResult { - // prettier-ignore - return ( - TypeGuard.TInteger(right) || TypeGuard.TNumber(right) ? TypeExtendsResult.True : - IsStructuralRight(right) ? StructuralRight(left, right) : - TypeGuard.TObject(right) ? TObjectRight(left, right) : - TypeGuard.TRecord(right) ? TRecordRight(left, right) : - TypeExtendsResult.False - ) - } - // -------------------------------------------------------------------------- - // Intersect - // -------------------------------------------------------------------------- - function TIntersectRight(left: TSchema, right: TIntersect): TypeExtendsResult { - // prettier-ignore - return right.allOf.every((schema) => Visit(left, schema) === TypeExtendsResult.True) - ? TypeExtendsResult.True - : TypeExtendsResult.False - } - function TIntersect(left: TIntersect, right: TSchema) { - // prettier-ignore - return left.allOf.some((schema) => Visit(schema, right) === TypeExtendsResult.True) - ? TypeExtendsResult.True - : TypeExtendsResult.False - } - // -------------------------------------------------------------------------- - // Iterator - // -------------------------------------------------------------------------- - function TIterator(left: TIterator, right: TSchema) { - // prettier-ignore - return ( - IsStructuralRight(right) ? StructuralRight(left, right) : - !TypeGuard.TIterator(right) ? TypeExtendsResult.False : - IntoBooleanResult(Visit(left.items, right.items)) - ) - } - // -------------------------------------------------------------------------- - // Literal - // -------------------------------------------------------------------------- - function TLiteral(left: TLiteral, right: TSchema): TypeExtendsResult { - // prettier-ignore - return ( - TypeGuard.TLiteral(right) && right.const === left.const ? TypeExtendsResult.True : - IsStructuralRight(right) ? StructuralRight(left, right) : - TypeGuard.TObject(right) ? TObjectRight(left, right) : - TypeGuard.TRecord(right) ? TRecordRight(left, right) : - TypeGuard.TString(right) ? TStringRight(left, right) : - TypeGuard.TNumber(right) ? TNumberRight(left, right) : - TypeGuard.TInteger(right) ? TIntegerRight(left, right) : - TypeGuard.TBoolean(right) ? TBooleanRight(left, right) : - TypeExtendsResult.False - ) - } - // -------------------------------------------------------------------------- - // Never - // -------------------------------------------------------------------------- - function TNeverRight(left: TSchema, right: TNever) { - return TypeExtendsResult.False - } - function TNever(left: TNever, right: TSchema) { - return TypeExtendsResult.True - } - // -------------------------------------------------------------------------- - // Not - // -------------------------------------------------------------------------- - function UnwrapTNot(schema: T): TUnknown | TNot['not'] { - let [current, depth]: [TSchema, number] = [schema, 0] - while (true) { - if (!TypeGuard.TNot(current)) break - current = current.not - depth += 1 - } - return depth % 2 === 0 ? current : Type.Unknown() - } - function TNot(left: TSchema, right: TSchema) { - // TypeScript has no concept of negated types, and attempts to correctly check the negated - // type at runtime would put TypeBox at odds with TypeScripts ability to statically infer - // the type. Instead we unwrap to either unknown or T and continue evaluating. - // prettier-ignore - return ( - TypeGuard.TNot(left) ? Visit(UnwrapTNot(left), right) : - TypeGuard.TNot(right) ? Visit(left, UnwrapTNot(right)) : - Throw('Invalid fallthrough for Not') - ) - } - // -------------------------------------------------------------------------- - // Null - // -------------------------------------------------------------------------- - function TNull(left: TNull, right: TSchema) { - // prettier-ignore - return ( - IsStructuralRight(right) ? StructuralRight(left, right) : - TypeGuard.TObject(right) ? TObjectRight(left, right) : - TypeGuard.TRecord(right) ? TRecordRight(left, right) : - TypeGuard.TNull(right) ? TypeExtendsResult.True : - TypeExtendsResult.False - ) - } - // -------------------------------------------------------------------------- - // Number - // -------------------------------------------------------------------------- - function TNumberRight(left: TSchema, right: TNumber) { - // prettier-ignore - return ( - TypeGuard.TLiteralNumber(left) ? TypeExtendsResult.True : - TypeGuard.TNumber(left) || TypeGuard.TInteger(left) ? TypeExtendsResult.True : - TypeExtendsResult.False - ) - } - function TNumber(left: TNumber, right: TSchema): TypeExtendsResult { - // prettier-ignore - return ( - IsStructuralRight(right) ? StructuralRight(left, right) : - TypeGuard.TObject(right) ? TObjectRight(left, right) : - TypeGuard.TRecord(right) ? TRecordRight(left, right) : - TypeGuard.TInteger(right) || TypeGuard.TNumber(right) ? TypeExtendsResult.True : - TypeExtendsResult.False - ) - } - // -------------------------------------------------------------------------- - // Object - // -------------------------------------------------------------------------- - function IsObjectPropertyCount(schema: TObject, count: number) { - return Object.getOwnPropertyNames(schema.properties).length === count - } - function IsObjectStringLike(schema: TObject) { - return IsObjectArrayLike(schema) - } - function IsObjectSymbolLike(schema: TObject) { - // prettier-ignore - return IsObjectPropertyCount(schema, 0) || ( - IsObjectPropertyCount(schema, 1) && 'description' in schema.properties && TypeGuard.TUnion(schema.properties.description) && schema.properties.description.anyOf.length === 2 && (( - TypeGuard.TString(schema.properties.description.anyOf[0]) && - TypeGuard.TUndefined(schema.properties.description.anyOf[1]) - ) || ( - TypeGuard.TString(schema.properties.description.anyOf[1]) && - TypeGuard.TUndefined(schema.properties.description.anyOf[0]) - )) - ) - } - function IsObjectNumberLike(schema: TObject) { - return IsObjectPropertyCount(schema, 0) - } - function IsObjectBooleanLike(schema: TObject) { - return IsObjectPropertyCount(schema, 0) - } - function IsObjectBigIntLike(schema: TObject) { - return IsObjectPropertyCount(schema, 0) - } - function IsObjectDateLike(schema: TObject) { - return IsObjectPropertyCount(schema, 0) - } - function IsObjectUint8ArrayLike(schema: TObject) { - return IsObjectArrayLike(schema) - } - function IsObjectFunctionLike(schema: TObject) { - const length = Type.Number() - return IsObjectPropertyCount(schema, 0) || (IsObjectPropertyCount(schema, 1) && 'length' in schema.properties && IntoBooleanResult(Visit(schema.properties['length'], length)) === TypeExtendsResult.True) - } - function IsObjectConstructorLike(schema: TObject) { - return IsObjectPropertyCount(schema, 0) - } - function IsObjectArrayLike(schema: TObject) { - const length = Type.Number() - return IsObjectPropertyCount(schema, 0) || (IsObjectPropertyCount(schema, 1) && 'length' in schema.properties && IntoBooleanResult(Visit(schema.properties['length'], length)) === TypeExtendsResult.True) - } - function IsObjectPromiseLike(schema: TObject) { - const then = Type.Function([Type.Any()], Type.Any()) - return IsObjectPropertyCount(schema, 0) || (IsObjectPropertyCount(schema, 1) && 'then' in schema.properties && IntoBooleanResult(Visit(schema.properties['then'], then)) === TypeExtendsResult.True) - } - // -------------------------------------------------------------------------- - // Property - // -------------------------------------------------------------------------- - function Property(left: TSchema, right: TSchema) { - // prettier-ignore - return ( - Visit(left, right) === TypeExtendsResult.False ? TypeExtendsResult.False : - TypeGuard.TOptional(left) && !TypeGuard.TOptional(right) ? TypeExtendsResult.False : - TypeExtendsResult.True - ) - } - function TObjectRight(left: TSchema, right: TObject) { - // prettier-ignore - return ( - TypeGuard.TUnknown(left) ? TypeExtendsResult.False : - TypeGuard.TAny(left) ? TypeExtendsResult.Union : ( - TypeGuard.TNever(left) || - (TypeGuard.TLiteralString(left) && IsObjectStringLike(right)) || - (TypeGuard.TLiteralNumber(left) && IsObjectNumberLike(right)) || - (TypeGuard.TLiteralBoolean(left) && IsObjectBooleanLike(right)) || - (TypeGuard.TSymbol(left) && IsObjectSymbolLike(right)) || - (TypeGuard.TBigInt(left) && IsObjectBigIntLike(right)) || - (TypeGuard.TString(left) && IsObjectStringLike(right)) || - (TypeGuard.TSymbol(left) && IsObjectSymbolLike(right)) || - (TypeGuard.TNumber(left) && IsObjectNumberLike(right)) || - (TypeGuard.TInteger(left) && IsObjectNumberLike(right)) || - (TypeGuard.TBoolean(left) && IsObjectBooleanLike(right)) || - (TypeGuard.TUint8Array(left) && IsObjectUint8ArrayLike(right)) || - (TypeGuard.TDate(left) && IsObjectDateLike(right)) || - (TypeGuard.TConstructor(left) && IsObjectConstructorLike(right)) || - (TypeGuard.TFunction(left) && IsObjectFunctionLike(right)) - ) ? TypeExtendsResult.True : - (TypeGuard.TRecord(left) && TypeGuard.TString(RecordKey(left))) ? (() => { - // When expressing a Record with literal key values, the Record is converted into a Object with - // the Hint assigned as `Record`. This is used to invert the extends logic. - return right[Hint] === 'Record' ? TypeExtendsResult.True : TypeExtendsResult.False - })() : - (TypeGuard.TRecord(left) && TypeGuard.TNumber(RecordKey(left))) ? (() => { - return IsObjectPropertyCount(right, 0) - ? TypeExtendsResult.True - : TypeExtendsResult.False - })() : - TypeExtendsResult.False - ) - } - function TObject(left: TObject, right: TSchema) { - // prettier-ignore - return ( - IsStructuralRight(right) ? StructuralRight(left, right) : - TypeGuard.TRecord(right) ? TRecordRight(left, right) : - !TypeGuard.TObject(right) ? TypeExtendsResult.False : - (() => { - for (const key of Object.getOwnPropertyNames(right.properties)) { - if (!(key in left.properties) && !TypeGuard.TOptional(right.properties[key])) { - return TypeExtendsResult.False - } - if(TypeGuard.TOptional(right.properties[key])) { - return TypeExtendsResult.True - } - if (Property(left.properties[key], right.properties[key]) === TypeExtendsResult.False) { - return TypeExtendsResult.False - } - } - return TypeExtendsResult.True - })() - ) - } - // -------------------------------------------------------------------------- - // Promise - // -------------------------------------------------------------------------- - function TPromise(left: TPromise, right: TSchema) { - // prettier-ignore - return ( - IsStructuralRight(right) ? StructuralRight(left, right) : - TypeGuard.TObject(right) && IsObjectPromiseLike(right) ? TypeExtendsResult.True : - !TypeGuard.TPromise(right) ? TypeExtendsResult.False : - IntoBooleanResult(Visit(left.item, right.item)) - ) - } - // -------------------------------------------------------------------------- - // Record - // -------------------------------------------------------------------------- - function RecordKey(schema: TRecord) { - // prettier-ignore - return ( - PatternNumberExact in schema.patternProperties ? Type.Number() : - PatternStringExact in schema.patternProperties ? Type.String() : - Throw('Unknown record key pattern') - ) - } - function RecordValue(schema: TRecord) { - // prettier-ignore - return ( - PatternNumberExact in schema.patternProperties ? schema.patternProperties[PatternNumberExact] : - PatternStringExact in schema.patternProperties ? schema.patternProperties[PatternStringExact] : - Throw('Unable to get record value schema') - ) - } - function TRecordRight(left: TSchema, right: TRecord) { - const [Key, Value] = [RecordKey(right), RecordValue(right)] - // prettier-ignore - return ( - (TypeGuard.TLiteralString(left) && TypeGuard.TNumber(Key) && IntoBooleanResult(Visit(left, Value)) === TypeExtendsResult.True) ? TypeExtendsResult.True : - TypeGuard.TUint8Array(left) && TypeGuard.TNumber(Key) ? Visit(left, Value) : - TypeGuard.TString(left) && TypeGuard.TNumber(Key) ? Visit(left, Value) : - TypeGuard.TArray(left) && TypeGuard.TNumber(Key) ? Visit(left, Value) : - TypeGuard.TObject(left) ? (() => { - for (const key of Object.getOwnPropertyNames(left.properties)) { - if (Property(Value, left.properties[key]) === TypeExtendsResult.False) { - return TypeExtendsResult.False - } - } - return TypeExtendsResult.True - })() : - TypeExtendsResult.False - ) - } - function TRecord(left: TRecord, right: TSchema) { - // prettier-ignore - return ( - IsStructuralRight(right) ? StructuralRight(left, right) : - TypeGuard.TObject(right) ? TObjectRight(left, right) : - !TypeGuard.TRecord(right) ? TypeExtendsResult.False : - Visit(RecordValue(left), RecordValue(right)) - ) - } - // -------------------------------------------------------------------------- - // String - // -------------------------------------------------------------------------- - function TStringRight(left: TSchema, right: TString) { - // prettier-ignore - return ( - TypeGuard.TLiteral(left) && ValueGuard.IsString(left.const) ? TypeExtendsResult.True : - TypeGuard.TString(left) ? TypeExtendsResult.True : - TypeExtendsResult.False - ) - } - function TString(left: TString, right: TSchema): TypeExtendsResult { - // prettier-ignore - return ( - IsStructuralRight(right) ? StructuralRight(left, right) : - TypeGuard.TObject(right) ? TObjectRight(left, right) : - TypeGuard.TRecord(right) ? TRecordRight(left, right) : - TypeGuard.TString(right) ? TypeExtendsResult.True : - TypeExtendsResult.False - ) - } - // -------------------------------------------------------------------------- - // Symbol - // -------------------------------------------------------------------------- - function TSymbol(left: TSymbol, right: TSchema): TypeExtendsResult { - // prettier-ignore - return ( - IsStructuralRight(right) ? StructuralRight(left, right) : - TypeGuard.TObject(right) ? TObjectRight(left, right) : - TypeGuard.TRecord(right) ? TRecordRight(left, right) : - TypeGuard.TSymbol(right) ? TypeExtendsResult.True : - TypeExtendsResult.False - ) - } - // -------------------------------------------------------------------------- - // TemplateLiteral - // -------------------------------------------------------------------------- - function TTemplateLiteral(left: TSchema, right: TSchema) { - // TemplateLiteral types are resolved to either unions for finite expressions or string - // for infinite expressions. Here we call to TemplateLiteralResolver to resolve for - // either type and continue evaluating. - // prettier-ignore - return ( - TypeGuard.TTemplateLiteral(left) ? Visit(TemplateLiteralResolver.Resolve(left), right) : - TypeGuard.TTemplateLiteral(right) ? Visit(left, TemplateLiteralResolver.Resolve(right)) : - Throw('Invalid fallthrough for TemplateLiteral') - ) - } - // -------------------------------------------------------------------------- - // Tuple - // -------------------------------------------------------------------------- - function IsArrayOfTuple(left: TTuple, right: TSchema) { - // prettier-ignore - return ( - TypeGuard.TArray(right) && - left.items !== undefined && - left.items.every((schema) => Visit(schema, right.items) === TypeExtendsResult.True) - ) - } - function TTupleRight(left: TSchema, right: TTuple) { - // prettier-ignore - return ( - TypeGuard.TNever(left) ? TypeExtendsResult.True : - TypeGuard.TUnknown(left) ? TypeExtendsResult.False : - TypeGuard.TAny(left) ? TypeExtendsResult.Union : - TypeExtendsResult.False - ) - } - function TTuple(left: TTuple, right: TSchema): TypeExtendsResult { - // prettier-ignore - return ( - IsStructuralRight(right) ? StructuralRight(left, right) : - TypeGuard.TObject(right) && IsObjectArrayLike(right) ? TypeExtendsResult.True : - TypeGuard.TArray(right) && IsArrayOfTuple(left, right) ? TypeExtendsResult.True : - !TypeGuard.TTuple(right) ? TypeExtendsResult.False : - (ValueGuard.IsUndefined(left.items) && !ValueGuard.IsUndefined(right.items)) || (!ValueGuard.IsUndefined(left.items) && ValueGuard.IsUndefined(right.items)) ? TypeExtendsResult.False : - (ValueGuard.IsUndefined(left.items) && !ValueGuard.IsUndefined(right.items)) ? TypeExtendsResult.True : - left.items!.every((schema, index) => Visit(schema, right.items![index]) === TypeExtendsResult.True) ? TypeExtendsResult.True : - TypeExtendsResult.False - ) - } - // -------------------------------------------------------------------------- - // Uint8Array - // -------------------------------------------------------------------------- - function TUint8Array(left: TUint8Array, right: TSchema) { - // prettier-ignore - return ( - IsStructuralRight(right) ? StructuralRight(left, right) : - TypeGuard.TObject(right) ? TObjectRight(left, right) : - TypeGuard.TRecord(right) ? TRecordRight(left, right) : - TypeGuard.TUint8Array(right) ? TypeExtendsResult.True : - TypeExtendsResult.False - ) - } - // -------------------------------------------------------------------------- - // Undefined - // -------------------------------------------------------------------------- - function TUndefined(left: TUndefined, right: TSchema) { - // prettier-ignore - return ( - IsStructuralRight(right) ? StructuralRight(left, right) : - TypeGuard.TObject(right) ? TObjectRight(left, right) : - TypeGuard.TRecord(right) ? TRecordRight(left, right) : - TypeGuard.TVoid(right) ? VoidRight(left, right) : - TypeGuard.TUndefined(right) ? TypeExtendsResult.True : - TypeExtendsResult.False - ) - } - // -------------------------------------------------------------------------- - // Union - // -------------------------------------------------------------------------- - function TUnionRight(left: TSchema, right: TUnion): TypeExtendsResult { - // prettier-ignore - return right.anyOf.some((schema) => Visit(left, schema) === TypeExtendsResult.True) - ? TypeExtendsResult.True - : TypeExtendsResult.False - } - function TUnion(left: TUnion, right: TSchema): TypeExtendsResult { - // prettier-ignore - return left.anyOf.every((schema) => Visit(schema, right) === TypeExtendsResult.True) - ? TypeExtendsResult.True - : TypeExtendsResult.False - } - // -------------------------------------------------------------------------- - // Unknown - // -------------------------------------------------------------------------- - function TUnknownRight(left: TSchema, right: TUnknown) { - return TypeExtendsResult.True - } - function TUnknown(left: TUnknown, right: TSchema) { - // prettier-ignore - return ( - TypeGuard.TNever(right) ? TNeverRight(left, right) : - TypeGuard.TIntersect(right) ? TIntersectRight(left, right) : - TypeGuard.TUnion(right) ? TUnionRight(left, right) : - TypeGuard.TAny(right) ? TAnyRight(left, right) : - TypeGuard.TString(right) ? TStringRight(left, right) : - TypeGuard.TNumber(right) ? TNumberRight(left, right) : - TypeGuard.TInteger(right) ? TIntegerRight(left, right) : - TypeGuard.TBoolean(right) ? TBooleanRight(left, right) : - TypeGuard.TArray(right) ? TArrayRight(left, right) : - TypeGuard.TTuple(right) ? TTupleRight(left, right) : - TypeGuard.TObject(right) ? TObjectRight(left, right) : - TypeGuard.TUnknown(right) ? TypeExtendsResult.True : - TypeExtendsResult.False - ) - } - // -------------------------------------------------------------------------- - // Void - // -------------------------------------------------------------------------- - function VoidRight(left: TSchema, right: TVoid) { - // prettier-ignore - return TypeGuard.TUndefined(left) ? TypeExtendsResult.True : - TypeGuard.TUndefined(left) ? TypeExtendsResult.True : - TypeExtendsResult.False - } - function TVoid(left: TVoid, right: TSchema) { - // prettier-ignore - return TypeGuard.TIntersect(right) ? TIntersectRight(left, right) : - TypeGuard.TUnion(right) ? TUnionRight(left, right) : - TypeGuard.TUnknown(right) ? TUnknownRight(left, right) : - TypeGuard.TAny(right) ? TAnyRight(left, right) : - TypeGuard.TObject(right) ? TObjectRight(left, right) : - TypeGuard.TVoid(right) ? TypeExtendsResult.True : - TypeExtendsResult.False - } - function Visit(left: TSchema, right: TSchema): TypeExtendsResult { - // prettier-ignore - return ( - // resolvable - (TypeGuard.TTemplateLiteral(left) || TypeGuard.TTemplateLiteral(right)) ? TTemplateLiteral(left, right) : - (TypeGuard.TNot(left) || TypeGuard.TNot(right)) ? TNot(left, right) : - // standard - TypeGuard.TAny(left) ? TAny(left, right) : - TypeGuard.TArray(left) ? TArray(left, right) : - TypeGuard.TBigInt(left) ? TBigInt(left, right) : - TypeGuard.TBoolean(left) ? TBoolean(left, right) : - TypeGuard.TAsyncIterator(left) ? TAsyncIterator(left, right) : - TypeGuard.TConstructor(left) ? TConstructor(left, right) : - TypeGuard.TDate(left) ? TDate(left, right) : - TypeGuard.TFunction(left) ? TFunction(left, right) : - TypeGuard.TInteger(left) ? TInteger(left, right) : - TypeGuard.TIntersect(left) ? TIntersect(left, right) : - TypeGuard.TIterator(left) ? TIterator(left, right) : - TypeGuard.TLiteral(left) ? TLiteral(left, right) : - TypeGuard.TNever(left) ? TNever(left, right) : - TypeGuard.TNull(left) ? TNull(left, right) : - TypeGuard.TNumber(left) ? TNumber(left, right) : - TypeGuard.TObject(left) ? TObject(left, right) : - TypeGuard.TRecord(left) ? TRecord(left, right) : - TypeGuard.TString(left) ? TString(left, right) : - TypeGuard.TSymbol(left) ? TSymbol(left, right) : - TypeGuard.TTuple(left) ? TTuple(left, right) : - TypeGuard.TPromise(left) ? TPromise(left, right) : - TypeGuard.TUint8Array(left) ? TUint8Array(left, right) : - TypeGuard.TUndefined(left) ? TUndefined(left, right) : - TypeGuard.TUnion(left) ? TUnion(left, right) : - TypeGuard.TUnknown(left) ? TUnknown(left, right) : - TypeGuard.TVoid(left) ? TVoid(left, right) : - Throw(`Unknown left type operand '${left[Kind]}'`) - ) - } - export function Extends(left: TSchema, right: TSchema): TypeExtendsResult { - return Visit(left, right) - } -} -// -------------------------------------------------------------------------- -// TypeClone -// -------------------------------------------------------------------------- -/** Specialized Clone for Types */ -export namespace TypeClone { - function ArrayType(value: unknown[]) { - return (value as any).map((value: unknown) => Visit(value as any)) - } - function DateType(value: Date) { - return new Date(value.getTime()) - } - function Uint8ArrayType(value: Uint8Array) { - return new Uint8Array(value) - } - function ObjectType(value: Record) { - const clonedProperties = Object.getOwnPropertyNames(value).reduce((acc, key) => ({ ...acc, [key]: Visit(value[key]) }), {}) - const clonedSymbols = Object.getOwnPropertySymbols(value).reduce((acc, key) => ({ ...acc, [key]: Visit(value[key as any]) }), {}) - return { ...clonedProperties, ...clonedSymbols } - } - function Visit(value: unknown): any { - // prettier-ignore - return ( - ValueGuard.IsArray(value) ? ArrayType(value) : - ValueGuard.IsDate(value) ? DateType(value) : - ValueGuard.IsUint8Array(value) ? Uint8ArrayType(value) : - ValueGuard.IsObject(value) ? ObjectType(value) : - value - ) - } - /** Clones a Rest */ - export function Rest(schemas: [...T]): T { - return schemas.map((schema) => Type(schema)) as T - } - /** Clones a Type */ - export function Type(schema: T, options: SchemaOptions = {}): T { - return { ...Visit(schema), ...options } - } -} -// -------------------------------------------------------------------------- -// IndexedAccessor -// -------------------------------------------------------------------------- -export namespace IndexedAccessor { - function OptionalUnwrap(schema: TSchema[]): TSchema[] { - return schema.map((schema) => { - const { [Optional]: _, ...clone } = TypeClone.Type(schema) - return clone - }) - } - function IsIntersectOptional(schema: TSchema[]): boolean { - return schema.every((schema) => TypeGuard.TOptional(schema)) - } - function IsUnionOptional(schema: TSchema[]): boolean { - return schema.some((schema) => TypeGuard.TOptional(schema)) - } - function ResolveIntersect(schema: TIntersect): TSchema { - return IsIntersectOptional(schema.allOf) ? Type.Optional(Type.Intersect(OptionalUnwrap(schema.allOf))) : schema - } - function ResolveUnion(schema: TUnion): TSchema { - return IsUnionOptional(schema.anyOf) ? Type.Optional(Type.Union(OptionalUnwrap(schema.anyOf))) : schema - } - function ResolveOptional(schema: TSchema) { - // prettier-ignore - return schema[Kind] === 'Intersect' ? ResolveIntersect(schema as TIntersect) : - schema[Kind] === 'Union' ? ResolveUnion(schema as TUnion) : - schema - } - function TIntersect(schema: TIntersect, key: string): TSchema { - const resolved = schema.allOf.reduce((acc, schema) => { - const indexed = Visit(schema, key) - return indexed[Kind] === 'Never' ? acc : [...acc, indexed] - }, [] as TSchema[]) - return ResolveOptional(Type.Intersect(resolved)) - } - function TUnion(schema: TUnion, key: string): TSchema { - const resolved = schema.anyOf.map((schema) => Visit(schema, key)) - return ResolveOptional(Type.Union(resolved)) - } - function TObject(schema: TObject, key: string): TSchema { - const property = schema.properties[key] - return ValueGuard.IsUndefined(property) ? Type.Never() : Type.Union([property]) - } - function TTuple(schema: TTuple, key: string): TSchema { - const items = schema.items - if (ValueGuard.IsUndefined(items)) return Type.Never() - const element = items[key as any as number] // - if (ValueGuard.IsUndefined(element)) return Type.Never() - return element - } - function Visit(schema: TSchema, key: string): TSchema { - // prettier-ignore - return schema[Kind] === 'Intersect' ? TIntersect(schema as TIntersect, key) : - schema[Kind] === 'Union' ? TUnion(schema as TUnion, key) : - schema[Kind] === 'Object' ? TObject(schema as TObject, key) : - schema[Kind] === 'Tuple' ? TTuple(schema as TTuple, key) : - Type.Never() - } - export function Resolve(schema: TSchema, keys: TPropertyKey[], options: SchemaOptions = {}): TSchema { - const resolved = keys.map((key) => Visit(schema, key.toString())) - return ResolveOptional(Type.Union(resolved, options)) - } -} -// -------------------------------------------------------------------------- -// Intrinsic -// -------------------------------------------------------------------------- -export namespace Intrinsic { - function Uncapitalize(value: string): string { - const [first, rest] = [value.slice(0, 1), value.slice(1)] - return `${first.toLowerCase()}${rest}` - } - function Capitalize(value: string): string { - const [first, rest] = [value.slice(0, 1), value.slice(1)] - return `${first.toUpperCase()}${rest}` - } - function Uppercase(value: string): string { - return value.toUpperCase() - } - function Lowercase(value: string): string { - return value.toLowerCase() - } - function IntrinsicTemplateLiteral(schema: TTemplateLiteral, mode: TIntrinsicMode) { - // note: template literals require special runtime handling as they are encoded in string patterns. - // This diverges from the mapped type which would otherwise map on the template literal kind. - const expression = TemplateLiteralParser.ParseExact(schema.pattern) - const finite = TemplateLiteralFinite.Check(expression) - if (!finite) return { ...schema, pattern: IntrinsicLiteral(schema.pattern, mode) } as any - const strings = [...TemplateLiteralGenerator.Generate(expression)] - const literals = strings.map((value) => Type.Literal(value)) - const mapped = IntrinsicRest(literals as any, mode) - const union = Type.Union(mapped) - return Type.TemplateLiteral([union]) - } - function IntrinsicLiteral(value: TLiteralValue, mode: TIntrinsicMode) { - // prettier-ignore - return typeof value === 'string' ? ( - mode === 'Uncapitalize' ? Uncapitalize(value) : - mode === 'Capitalize' ? Capitalize(value) : - mode === 'Uppercase' ? Uppercase(value) : - mode === 'Lowercase' ? Lowercase(value) : - value) : value.toString() - } - function IntrinsicRest(schema: TSchema[], mode: TIntrinsicMode): TSchema[] { - if (schema.length === 0) return [] - const [L, ...R] = schema - return [Map(L, mode), ...IntrinsicRest(R, mode)] - } - function Visit(schema: TSchema, mode: TIntrinsicMode) { - // prettier-ignore - return TypeGuard.TTemplateLiteral(schema) ? IntrinsicTemplateLiteral(schema, mode) : - TypeGuard.TUnion(schema) ? Type.Union(IntrinsicRest(schema.anyOf, mode)) : - TypeGuard.TLiteral(schema) ? Type.Literal(IntrinsicLiteral(schema.const, mode)) : - schema - } - /** Applies an intrinsic string manipulation to the given type. */ - export function Map(schema: T, mode: M): TIntrinsic { - return Visit(schema, mode) - } -} -// -------------------------------------------------------------------------- -// ObjectMap -// -------------------------------------------------------------------------- -export namespace ObjectMap { - function TIntersect(schema: TIntersect, callback: (object: TObject) => TObject) { - // prettier-ignore - return Type.Intersect(schema.allOf.map((inner) => Visit(inner, callback)), { ...schema }) - } - function TUnion(schema: TUnion, callback: (object: TObject) => TObject) { - // prettier-ignore - return Type.Union(schema.anyOf.map((inner) => Visit(inner, callback)), { ...schema }) - } - function TObject(schema: TObject, callback: (object: TObject) => TObject) { - return callback(schema) - } - function Visit(schema: TSchema, callback: (object: TObject) => TObject): TSchema { - // There are cases where users need to map objects with unregistered kinds. Using a TypeGuard here would - // prevent sub schema mapping as unregistered kinds will not pass TSchema checks. This is notable in the - // case of TObject where unregistered property kinds cause the TObject check to fail. As mapping is only - // used for composition, we use explicit checks instead. - // prettier-ignore - return ( - schema[Kind] === 'Intersect' ? TIntersect(schema as TIntersect, callback) : - schema[Kind] === 'Union' ? TUnion(schema as TUnion, callback) : - schema[Kind] === 'Object' ? TObject(schema as TObject, callback) : - schema - ) - } - export function Map(schema: TSchema, callback: (object: TObject) => TObject, options: SchemaOptions): T { - return { ...Visit(TypeClone.Type(schema), callback), ...options } as unknown as T - } -} -// -------------------------------------------------------------------------- -// KeyResolver -// -------------------------------------------------------------------------- -export interface KeyResolverOptions { - includePatterns: boolean -} -export namespace KeyResolver { - function UnwrapPattern(key: string) { - return key[0] === '^' && key[key.length - 1] === '$' ? key.slice(1, key.length - 1) : key - } - function TIntersect(schema: TIntersect, options: KeyResolverOptions): string[] { - return schema.allOf.reduce((acc, schema) => [...acc, ...Visit(schema, options)], [] as string[]) - } - function TUnion(schema: TUnion, options: KeyResolverOptions): string[] { - const sets = schema.anyOf.map((inner) => Visit(inner, options)) - return [...sets.reduce((set, outer) => outer.map((key) => (sets.every((inner) => inner.includes(key)) ? set.add(key) : set))[0], new Set())] - } - function TObject(schema: TObject, options: KeyResolverOptions): string[] { - return Object.getOwnPropertyNames(schema.properties) - } - function TRecord(schema: TRecord, options: KeyResolverOptions): string[] { - return options.includePatterns ? Object.getOwnPropertyNames(schema.patternProperties) : [] - } - function Visit(schema: TSchema, options: KeyResolverOptions): string[] { - // prettier-ignore - return ( - TypeGuard.TIntersect(schema) ? TIntersect(schema, options) : - TypeGuard.TUnion(schema) ? TUnion(schema, options) : - TypeGuard.TObject(schema) ? TObject(schema, options) : - TypeGuard.TRecord(schema) ? TRecord(schema, options) : - [] - ) - } - /** Resolves an array of keys in this schema */ - export function ResolveKeys(schema: TSchema, options: KeyResolverOptions): string[] { - return [...new Set(Visit(schema, options))] - } - /** Resolves a regular expression pattern matching all keys in this schema */ - export function ResolvePattern(schema: TSchema): string { - const keys = ResolveKeys(schema, { includePatterns: true }) - const pattern = keys.map((key) => `(${UnwrapPattern(key)})`) - return `^(${pattern.join('|')})$` - } -} -// -------------------------------------------------------------------------- -// KeyArrayResolver -// -------------------------------------------------------------------------- -export class KeyArrayResolverError extends TypeBoxError {} -export namespace KeyArrayResolver { - /** Resolves an array of string[] keys from the given schema or array type. */ - export function Resolve(schema: TSchema | string[]): string[] { - // prettier-ignore - return Array.isArray(schema) ? schema : - TypeGuard.TUnionLiteral(schema) ? schema.anyOf.map((schema) => schema.const.toString()) : - TypeGuard.TLiteral(schema) ? [schema.const as string] : - TypeGuard.TTemplateLiteral(schema) ? (() => { - const expression = TemplateLiteralParser.ParseExact(schema.pattern) - if (!TemplateLiteralFinite.Check(expression)) throw new KeyArrayResolverError('Cannot resolve keys from infinite template expression') - return [...TemplateLiteralGenerator.Generate(expression)] - })() : [] - } -} -// -------------------------------------------------------------------------- -// UnionResolver -// -------------------------------------------------------------------------- -export namespace UnionResolver { - function* TUnion(union: TUnion): IterableIterator { - for (const schema of union.anyOf) { - if (schema[Kind] === 'Union') { - yield* TUnion(schema as TUnion) - } else { - yield schema - } - } - } - /** Returns a resolved union with interior unions flattened */ - export function Resolve(union: TUnion): TUnion { - return Type.Union([...TUnion(union)], { ...union }) - } -} -// -------------------------------------------------------------------------- -// TemplateLiteralPattern -// -------------------------------------------------------------------------- -export class TemplateLiteralPatternError extends TypeBoxError {} -export namespace TemplateLiteralPattern { - function Throw(message: string): never { - throw new TemplateLiteralPatternError(message) - } - function Escape(value: string) { - return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') - } - function Visit(schema: TSchema, acc: string): string { - // prettier-ignore - return ( - TypeGuard.TTemplateLiteral(schema) ? schema.pattern.slice(1, schema.pattern.length - 1) : - TypeGuard.TUnion(schema) ? `(${schema.anyOf.map((schema) => Visit(schema, acc)).join('|')})` : - TypeGuard.TNumber(schema) ? `${acc}${PatternNumber}` : - TypeGuard.TInteger(schema) ? `${acc}${PatternNumber}` : - TypeGuard.TBigInt(schema) ? `${acc}${PatternNumber}` : - TypeGuard.TString(schema) ? `${acc}${PatternString}` : - TypeGuard.TLiteral(schema) ? `${acc}${Escape(schema.const.toString())}` : - TypeGuard.TBoolean(schema) ? `${acc}${PatternBoolean}` : - Throw(`Unexpected Kind '${schema[Kind]}'`) - ) - } - export function Create(kinds: TTemplateLiteralKind[]): string { - return `^${kinds.map((schema) => Visit(schema, '')).join('')}\$` - } -} -// -------------------------------------------------------------------------------------- -// TemplateLiteralResolver -// -------------------------------------------------------------------------------------- -export namespace TemplateLiteralResolver { - /** Resolves a template literal as a TUnion */ - export function Resolve(template: TTemplateLiteral): TString | TUnion | TLiteral { - const expression = TemplateLiteralParser.ParseExact(template.pattern) - if (!TemplateLiteralFinite.Check(expression)) return Type.String() - const literals = [...TemplateLiteralGenerator.Generate(expression)].map((value) => Type.Literal(value)) - return Type.Union(literals) - } -} -// -------------------------------------------------------------------------------------- -// TemplateLiteralParser -// -------------------------------------------------------------------------------------- -export class TemplateLiteralParserError extends TypeBoxError {} -export namespace TemplateLiteralParser { - export type Expression = And | Or | Const - export type Const = { type: 'const'; const: string } - export type And = { type: 'and'; expr: Expression[] } - export type Or = { type: 'or'; expr: Expression[] } - function IsNonEscaped(pattern: string, index: number, char: string) { - return pattern[index] === char && pattern.charCodeAt(index - 1) !== 92 - } - function IsOpenParen(pattern: string, index: number) { - return IsNonEscaped(pattern, index, '(') - } - function IsCloseParen(pattern: string, index: number) { - return IsNonEscaped(pattern, index, ')') - } - function IsSeparator(pattern: string, index: number) { - return IsNonEscaped(pattern, index, '|') - } - function IsGroup(pattern: string) { - if (!(IsOpenParen(pattern, 0) && IsCloseParen(pattern, pattern.length - 1))) return false - let count = 0 - for (let index = 0; index < pattern.length; index++) { - if (IsOpenParen(pattern, index)) count += 1 - if (IsCloseParen(pattern, index)) count -= 1 - if (count === 0 && index !== pattern.length - 1) return false - } - return true - } - function InGroup(pattern: string) { - return pattern.slice(1, pattern.length - 1) - } - function IsPrecedenceOr(pattern: string) { - let count = 0 - for (let index = 0; index < pattern.length; index++) { - if (IsOpenParen(pattern, index)) count += 1 - if (IsCloseParen(pattern, index)) count -= 1 - if (IsSeparator(pattern, index) && count === 0) return true - } - return false - } - function IsPrecedenceAnd(pattern: string) { - for (let index = 0; index < pattern.length; index++) { - if (IsOpenParen(pattern, index)) return true - } - return false - } - function Or(pattern: string): Expression { - let [count, start] = [0, 0] - const expressions: Expression[] = [] - for (let index = 0; index < pattern.length; index++) { - if (IsOpenParen(pattern, index)) count += 1 - if (IsCloseParen(pattern, index)) count -= 1 - if (IsSeparator(pattern, index) && count === 0) { - const range = pattern.slice(start, index) - if (range.length > 0) expressions.push(Parse(range)) - start = index + 1 - } - } - const range = pattern.slice(start) - if (range.length > 0) expressions.push(Parse(range)) - if (expressions.length === 0) return { type: 'const', const: '' } - if (expressions.length === 1) return expressions[0] - return { type: 'or', expr: expressions } - } - function And(pattern: string): Expression { - function Group(value: string, index: number): [number, number] { - if (!IsOpenParen(value, index)) throw new TemplateLiteralParserError(`TemplateLiteralParser: Index must point to open parens`) - let count = 0 - for (let scan = index; scan < value.length; scan++) { - if (IsOpenParen(value, scan)) count += 1 - if (IsCloseParen(value, scan)) count -= 1 - if (count === 0) return [index, scan] - } - throw new TemplateLiteralParserError(`TemplateLiteralParser: Unclosed group parens in expression`) - } - function Range(pattern: string, index: number): [number, number] { - for (let scan = index; scan < pattern.length; scan++) { - if (IsOpenParen(pattern, scan)) return [index, scan] - } - return [index, pattern.length] - } - const expressions: Expression[] = [] - for (let index = 0; index < pattern.length; index++) { - if (IsOpenParen(pattern, index)) { - const [start, end] = Group(pattern, index) - const range = pattern.slice(start, end + 1) - expressions.push(Parse(range)) - index = end - } else { - const [start, end] = Range(pattern, index) - const range = pattern.slice(start, end) - if (range.length > 0) expressions.push(Parse(range)) - index = end - 1 - } - } - // prettier-ignore - return (expressions.length === 0) ? { type: 'const', const: '' } : - (expressions.length === 1) ? expressions[0] : - { type: 'and', expr: expressions } - } - /** Parses a pattern and returns an expression tree */ - export function Parse(pattern: string): Expression { - // prettier-ignore - return IsGroup(pattern) ? Parse(InGroup(pattern)) : - IsPrecedenceOr(pattern) ? Or(pattern) : - IsPrecedenceAnd(pattern) ? And(pattern) : - { type: 'const', const: pattern } - } - /** Parses a pattern and strips forward and trailing ^ and $ */ - export function ParseExact(pattern: string): Expression { - return Parse(pattern.slice(1, pattern.length - 1)) - } -} -// -------------------------------------------------------------------------------------- -// TemplateLiteralFinite -// -------------------------------------------------------------------------------------- -export class TemplateLiteralFiniteError extends TypeBoxError {} -export namespace TemplateLiteralFinite { - function Throw(message: string): never { - throw new TemplateLiteralFiniteError(message) - } - function IsNumber(expression: TemplateLiteralParser.Expression): boolean { - // prettier-ignore - return ( - expression.type === 'or' && - expression.expr.length === 2 && - expression.expr[0].type === 'const' && - expression.expr[0].const === '0' && - expression.expr[1].type === 'const' && - expression.expr[1].const === '[1-9][0-9]*' - ) - } - function IsBoolean(expression: TemplateLiteralParser.Expression): boolean { - // prettier-ignore - return ( - expression.type === 'or' && - expression.expr.length === 2 && - expression.expr[0].type === 'const' && - expression.expr[0].const === 'true' && - expression.expr[1].type === 'const' && - expression.expr[1].const === 'false' - ) - } - function IsString(expression: TemplateLiteralParser.Expression) { - return expression.type === 'const' && expression.const === '.*' - } - export function Check(expression: TemplateLiteralParser.Expression): boolean { - // prettier-ignore - return IsBoolean(expression) ? true : - IsNumber(expression) || IsString(expression) ? false : - (expression.type === 'and') ? expression.expr.every((expr) => Check(expr)) : - (expression.type === 'or') ? expression.expr.every((expr) => Check(expr)) : - (expression.type === 'const') ? true : - Throw(`Unknown expression type`) - } -} -// -------------------------------------------------------------------------------------- -// TemplateLiteralGenerator -// -------------------------------------------------------------------------------------- -export class TemplateLiteralGeneratorError extends TypeBoxError {} -export namespace TemplateLiteralGenerator { - function* Reduce(buffer: string[][]): IterableIterator { - if (buffer.length === 1) return yield* buffer[0] - for (const left of buffer[0]) { - for (const right of Reduce(buffer.slice(1))) { - yield `${left}${right}` - } - } - } - function* And(expression: TemplateLiteralParser.And): IterableIterator { - return yield* Reduce(expression.expr.map((expr) => [...Generate(expr)])) - } - function* Or(expression: TemplateLiteralParser.Or): IterableIterator { - for (const expr of expression.expr) yield* Generate(expr) - } - function* Const(expression: TemplateLiteralParser.Const): IterableIterator { - return yield expression.const - } - export function* Generate(expression: TemplateLiteralParser.Expression): IterableIterator { - // prettier-ignore - return ( - expression.type === 'and' ? yield* And(expression) : - expression.type === 'or' ? yield* Or(expression) : - expression.type === 'const' ? yield* Const(expression) : - (() => { throw new TemplateLiteralGeneratorError('Unknown expression') })() - ) - } -} -// --------------------------------------------------------------------- -// TemplateLiteralDslParser -// --------------------------------------------------------------------- -export namespace TemplateLiteralDslParser { - function* ParseUnion(template: string): IterableIterator { - const trim = template.trim().replace(/"|'/g, '') - // prettier-ignore - return ( - trim === 'boolean' ? yield Type.Boolean() : - trim === 'number' ? yield Type.Number() : - trim === 'bigint' ? yield Type.BigInt() : - trim === 'string' ? yield Type.String() : - yield (() => { - const literals = trim.split('|').map((literal) => Type.Literal(literal.trim())) - return ( - literals.length === 0 ? Type.Never() : - literals.length === 1 ? literals[0] : - Type.Union(literals) - ) - })() - ) - } - function* ParseTerminal(template: string): IterableIterator { - if (template[1] !== '{') { - const L = Type.Literal('$') - const R = ParseLiteral(template.slice(1)) - return yield* [L, ...R] - } - for (let i = 2; i < template.length; i++) { - if (template[i] === '}') { - const L = ParseUnion(template.slice(2, i)) - const R = ParseLiteral(template.slice(i + 1)) - return yield* [...L, ...R] - } - } - yield Type.Literal(template) - } - function* ParseLiteral(template: string): IterableIterator { - for (let i = 0; i < template.length; i++) { - if (template[i] === '$') { - const L = Type.Literal(template.slice(0, i)) - const R = ParseTerminal(template.slice(i)) - return yield* [L, ...R] - } - } - yield Type.Literal(template) - } - export function Parse(template_dsl: string): TTemplateLiteralKind[] { - return [...ParseLiteral(template_dsl)] - } -} -// --------------------------------------------------------------------- -// TransformBuilder -// --------------------------------------------------------------------- -export class TransformDecodeBuilder { - constructor(private readonly schema: T) {} - public Decode, U>>(decode: D): TransformEncodeBuilder { - return new TransformEncodeBuilder(this.schema, decode) - } -} -export class TransformEncodeBuilder { - constructor(private readonly schema: T, private readonly decode: D) {} - public Encode, StaticDecode>>(encode: E): TTransform> { - const schema = TypeClone.Type(this.schema) - // prettier-ignore - return ( - TypeGuard.TTransform(schema) ? (() => { - const Encode = (value: unknown) => schema[Transform].Encode(encode(value as any)) - const Decode = (value: unknown) => this.decode(schema[Transform].Decode(value)) - const Codec = { Encode: Encode, Decode: Decode } - return { ...schema, [Transform]: Codec } - })() : (() => { - const Codec = { Decode: this.decode, Encode: encode } - return { ...schema, [Transform]: Codec } - })() - ) as TTransform> - } -} -// -------------------------------------------------------------------------- -// TypeOrdinal: Used for auto $id generation -// -------------------------------------------------------------------------- -let TypeOrdinal = 0 -// -------------------------------------------------------------------------- -// TypeBuilder -// -------------------------------------------------------------------------- -export class TypeBuilderError extends TypeBoxError {} -export class TypeBuilder { - /** `[Internal]` Creates a schema without `static` and `params` types */ - protected Create(schema: Omit): T { - return schema as any - } - /** `[Internal]` Throws a TypeBuilder error with the given message */ - protected Throw(message: string): never { - throw new TypeBuilderError(message) - } - /** `[Internal]` Discards property keys from the given record type */ - protected Discard(record: Record, keys: PropertyKey[]) { - return keys.reduce((acc, key) => { - const { [key as any]: _, ...rest } = acc - return rest - }, record) as any - } - /** `[Json]` Omits compositing symbols from this schema */ - public Strict(schema: T): T { - return JSON.parse(JSON.stringify(schema)) - } -} -// -------------------------------------------------------------------------- -// JsonTypeBuilder -// -------------------------------------------------------------------------- -export class JsonTypeBuilder extends TypeBuilder { - // ------------------------------------------------------------------------ - // Modifiers - // ------------------------------------------------------------------------ - /** `[Json]` Creates a Readonly and Optional property */ - public ReadonlyOptional(schema: T): TReadonly> { - return this.Readonly(this.Optional(schema)) - } - /** `[Json]` Creates a Readonly property */ - public Readonly(schema: T): TReadonly { - return { ...TypeClone.Type(schema), [Readonly]: 'Readonly' } - } - /** `[Json]` Creates an Optional property */ - public Optional(schema: T): TOptional { - return { ...TypeClone.Type(schema), [Optional]: 'Optional' } - } - // ------------------------------------------------------------------------ - // Types - // ------------------------------------------------------------------------ - /** `[Json]` Creates an Any type */ - public Any(options: SchemaOptions = {}): TAny { - return this.Create({ ...options, [Kind]: 'Any' }) - } - /** `[Json]` Creates an Array type */ - public Array(schema: T, options: ArrayOptions = {}): TArray { - return this.Create({ ...options, [Kind]: 'Array', type: 'array', items: TypeClone.Type(schema) }) - } - /** `[Json]` Creates a Boolean type */ - public Boolean(options: SchemaOptions = {}): TBoolean { - return this.Create({ ...options, [Kind]: 'Boolean', type: 'boolean' }) - } - /** `[Json]` Intrinsic function to Capitalize LiteralString types */ - public Capitalize(schema: T, options: SchemaOptions = {}): TIntrinsic { - return { ...Intrinsic.Map(TypeClone.Type(schema), 'Capitalize'), ...options } - } - /** `[Json]` Creates a Composite object type */ - public Composite(objects: [...T], options?: ObjectOptions): TComposite { - const intersect: any = Type.Intersect(objects, {}) - const keys = KeyResolver.ResolveKeys(intersect, { includePatterns: false }) - const properties = keys.reduce((acc, key) => ({ ...acc, [key]: Type.Index(intersect, [key]) }), {} as TProperties) - return Type.Object(properties, options) as TComposite - } - /** `[Json]` Creates a Enum type */ - public Enum>(item: T, options: SchemaOptions = {}): TEnum { - if (ValueGuard.IsUndefined(item)) return this.Throw('Enum undefined or empty') - // prettier-ignore - const values1 = Object.getOwnPropertyNames(item).filter((key) => isNaN(key as any)).map((key) => item[key]) as T[keyof T][] - const values2 = [...new Set(values1)] - const anyOf = values2.map((value) => Type.Literal(value)) - return this.Union(anyOf, { ...options, [Hint]: 'Enum' }) as TEnum - } - /** `[Json]` Creates a Conditional type */ - public Extends(left: L, right: R, trueType: T, falseType: U, options: SchemaOptions = {}): TExtends { - switch (TypeExtends.Extends(left, right)) { - case TypeExtendsResult.Union: - return this.Union([TypeClone.Type(trueType, options), TypeClone.Type(falseType, options)]) as any as TExtends - case TypeExtendsResult.True: - return TypeClone.Type(trueType, options) as unknown as TExtends - case TypeExtendsResult.False: - return TypeClone.Type(falseType, options) as unknown as TExtends - } - } - /** `[Json]` Constructs a type by excluding from unionType all union members that are assignable to excludedMembers */ - public Exclude(unionType: L, excludedMembers: R, options: SchemaOptions = {}): TExclude { - // prettier-ignore - return ( - TypeGuard.TTemplateLiteral(unionType) ? this.Exclude(TemplateLiteralResolver.Resolve(unionType), excludedMembers, options) : - TypeGuard.TTemplateLiteral(excludedMembers) ? this.Exclude(unionType, TemplateLiteralResolver.Resolve(excludedMembers), options) : - TypeGuard.TUnion(unionType) ? (() => { - const narrowed = unionType.anyOf.filter((inner) => TypeExtends.Extends(inner, excludedMembers) === TypeExtendsResult.False) - return (narrowed.length === 1 ? TypeClone.Type(narrowed[0], options) : this.Union(narrowed, options)) as TExclude - })() : - TypeExtends.Extends(unionType, excludedMembers) !== TypeExtendsResult.False ? this.Never(options) : - TypeClone.Type(unionType, options) - ) as TExclude - } - /** `[Json]` Constructs a type by extracting from type all union members that are assignable to union */ - public Extract(type: L, union: R, options: SchemaOptions = {}): TExtract { - // prettier-ignore - return ( - TypeGuard.TTemplateLiteral(type) ? this.Extract(TemplateLiteralResolver.Resolve(type), union, options) : - TypeGuard.TTemplateLiteral(union) ? this.Extract(type, TemplateLiteralResolver.Resolve(union), options) : - TypeGuard.TUnion(type) ? (() => { - const narrowed = type.anyOf.filter((inner) => TypeExtends.Extends(inner, union) !== TypeExtendsResult.False) - return (narrowed.length === 1 ? TypeClone.Type(narrowed[0], options) : this.Union(narrowed, options)) - })() : - TypeExtends.Extends(type, union) !== TypeExtendsResult.False ? TypeClone.Type(type, options) : - this.Never(options) - ) as TExtract - } - /** `[Json]` Returns an Indexed property type for the given keys */ - public Index(schema: T, keys: K, options?: SchemaOptions): AssertType - /** `[Json]` Returns an Indexed property type for the given keys */ - public Index)[]>(schema: T, keys: [...K], options?: SchemaOptions): TIndex> - /** `[Json]` Returns an Indexed property type for the given keys */ - public Index(schema: T, keys: K, options?: SchemaOptions): UnionType> - /** `[Json]` Returns an Indexed property type for the given keys */ - public Index(schema: T, keys: K, options?: SchemaOptions): TIndex> - /** `[Json]` Returns an Indexed property type for the given keys */ - public Index>(schema: T, keys: K, options?: SchemaOptions): TIndex - /** `[Json]` Returns an Indexed property type for the given keys */ - public Index)[]>(schema: T, keys: [...K], options?: SchemaOptions): TIndex> - /** `[Json]` Returns an Indexed property type for the given keys */ - public Index[]>>(schema: T, keys: K, options?: SchemaOptions): TIndex> - /** `[Json]` Returns an Indexed property type for the given keys */ - public Index(schema: T, key: K, options?: SchemaOptions): TSchema - /** `[Json]` Returns an Indexed property type for the given keys */ - public Index(schema: TSchema, unresolved: any, options: SchemaOptions = {}): any { - // prettier-ignore - return ( - TypeGuard.TArray(schema) && TypeGuard.TNumber(unresolved) ? (() => { - return TypeClone.Type(schema.items, options) - })() : - TypeGuard.TTuple(schema) && TypeGuard.TNumber(unresolved) ? (() => { - const items = ValueGuard.IsUndefined(schema.items) ? [] : schema.items - const cloned = items.map((schema) => TypeClone.Type(schema)) - return this.Union(cloned, options) - })() : (() => { - const keys = KeyArrayResolver.Resolve(unresolved) - const clone = TypeClone.Type(schema) - return IndexedAccessor.Resolve(clone, keys, options) - })() - ) - } - /** `[Json]` Creates an Integer type */ - public Integer(options: NumericOptions = {}): TInteger { - return this.Create({ ...options, [Kind]: 'Integer', type: 'integer' }) - } - /** `[Json]` Creates an Intersect type */ - public Intersect(allOf: [], options?: SchemaOptions): TNever - /** `[Json]` Creates an Intersect type */ - public Intersect(allOf: [...T], options?: SchemaOptions): T[0] - /** `[Json]` Creates an Intersect type */ - public Intersect(allOf: [...T], options?: IntersectOptions): TIntersect - /** `[Json]` Creates an Intersect type */ - public Intersect(allOf: TSchema[], options: IntersectOptions = {}) { - if (allOf.length === 0) return Type.Never() - if (allOf.length === 1) return TypeClone.Type(allOf[0], options) - if (allOf.some((schema) => TypeGuard.TTransform(schema))) this.Throw('Cannot intersect transform types') - const objects = allOf.every((schema) => TypeGuard.TObject(schema)) - const cloned = TypeClone.Rest(allOf) - // prettier-ignore - const clonedUnevaluatedProperties = TypeGuard.TSchema(options.unevaluatedProperties) - ? { unevaluatedProperties: TypeClone.Type(options.unevaluatedProperties) } - : {} - return options.unevaluatedProperties === false || TypeGuard.TSchema(options.unevaluatedProperties) || objects - ? this.Create({ ...options, ...clonedUnevaluatedProperties, [Kind]: 'Intersect', type: 'object', allOf: cloned }) - : this.Create({ ...options, ...clonedUnevaluatedProperties, [Kind]: 'Intersect', allOf: cloned }) - } - /** `[Json]` Creates a KeyOf type */ - public KeyOf(schema: T, options: SchemaOptions = {}): TKeyOf { - // prettier-ignore - return ( - TypeGuard.TRecord(schema) ? (() => { - const pattern = Object.getOwnPropertyNames(schema.patternProperties)[0] - return ( - pattern === PatternNumberExact ? this.Number(options) : - pattern === PatternStringExact ? this.String(options) : - this.Throw('Unable to resolve key type from Record key pattern') - ) - })() : - TypeGuard.TTuple(schema) ? (() => { - const items = ValueGuard.IsUndefined(schema.items) ? [] : schema.items - const literals = items.map((_, index) => Type.Literal(index.toString())) - return this.Union(literals, options) - })() : - TypeGuard.TArray(schema) ? (() => { - return this.Number(options) - })() : (() => { - const keys = KeyResolver.ResolveKeys(schema, { includePatterns: false }) - if (keys.length === 0) return this.Never(options) as TKeyOf - const literals = keys.map((key) => this.Literal(key)) - return this.Union(literals, options) - })() - ) as unknown as TKeyOf - } - /** `[Json]` Creates a Literal type */ - public Literal(value: T, options: SchemaOptions = {}): TLiteral { - return this.Create({ ...options, [Kind]: 'Literal', const: value, type: typeof value as 'string' | 'number' | 'boolean' }) - } - /** `[Json]` Intrinsic function to Lowercase LiteralString types */ - public Lowercase(schema: T, options: SchemaOptions = {}): TIntrinsic { - return { ...Intrinsic.Map(TypeClone.Type(schema), 'Lowercase'), ...options } - } - /** `[Json]` Creates a Never type */ - public Never(options: SchemaOptions = {}): TNever { - return this.Create({ ...options, [Kind]: 'Never', not: {} }) - } - /** `[Json]` Creates a Not type */ - public Not(schema: T, options?: SchemaOptions): TNot { - return this.Create({ ...options, [Kind]: 'Not', not: TypeClone.Type(schema) }) - } - /** `[Json]` Creates a Null type */ - public Null(options: SchemaOptions = {}): TNull { - return this.Create({ ...options, [Kind]: 'Null', type: 'null' }) - } - /** `[Json]` Creates a Number type */ - public Number(options: NumericOptions = {}): TNumber { - return this.Create({ ...options, [Kind]: 'Number', type: 'number' }) - } - /** `[Json]` Creates an Object type */ - public Object(properties: T, options: ObjectOptions = {}): TObject { - const propertyKeys = Object.getOwnPropertyNames(properties) - const optionalKeys = propertyKeys.filter((key) => TypeGuard.TOptional(properties[key])) - const requiredKeys = propertyKeys.filter((name) => !optionalKeys.includes(name)) - const clonedAdditionalProperties = TypeGuard.TSchema(options.additionalProperties) ? { additionalProperties: TypeClone.Type(options.additionalProperties) } : {} - const clonedProperties = propertyKeys.reduce((acc, key) => ({ ...acc, [key]: TypeClone.Type(properties[key]) }), {} as TProperties) - return requiredKeys.length > 0 - ? this.Create({ ...options, ...clonedAdditionalProperties, [Kind]: 'Object', type: 'object', properties: clonedProperties, required: requiredKeys }) - : this.Create({ ...options, ...clonedAdditionalProperties, [Kind]: 'Object', type: 'object', properties: clonedProperties }) - } - /** `[Json]` Constructs a type whose keys are omitted from the given type */ - public Omit)[]>(schema: T, keys: readonly [...K], options?: SchemaOptions): TOmit - /** `[Json]` Constructs a type whose keys are omitted from the given type */ - public Omit[]>>(schema: T, keys: K, options?: SchemaOptions): TOmit[number]> - /** `[Json]` Constructs a type whose keys are omitted from the given type */ - public Omit>(schema: T, key: K, options?: SchemaOptions): TOmit - /** `[Json]` Constructs a type whose keys are omitted from the given type */ - public Omit(schema: T, key: K, options?: SchemaOptions): TOmit[number]> - /** `[Json]` Constructs a type whose keys are omitted from the given type */ - public Omit(schema: T, key: K, options?: SchemaOptions): TOmit - /** `[Json]` Constructs a type whose keys are omitted from the given type */ - public Omit(schema: TSchema, unresolved: any, options: SchemaOptions = {}): any { - const keys = KeyArrayResolver.Resolve(unresolved) - // prettier-ignore - return ObjectMap.Map(this.Discard(TypeClone.Type(schema), ['$id', Transform]), (object) => { - if (ValueGuard.IsArray(object.required)) { - object.required = object.required.filter((key: string) => !keys.includes(key as any)) - if (object.required.length === 0) delete object.required - } - for (const key of Object.getOwnPropertyNames(object.properties)) { - if (keys.includes(key as any)) delete object.properties[key] - } - return this.Create(object) - }, options) - } - /** `[Json]` Constructs a type where all properties are optional */ - public Partial(schema: T, options: ObjectOptions = {}): TPartial { - // prettier-ignore - return ObjectMap.Map(this.Discard(TypeClone.Type(schema), ['$id', Transform]), (object) => { - const properties = Object.getOwnPropertyNames(object.properties).reduce((acc, key) => { - return { ...acc, [key]: this.Optional(object.properties[key]) } - }, {} as TProperties) - return this.Object(properties, this.Discard(object, ['required']) /* object used as options to retain other constraints */) - }, options) - } - /** `[Json]` Constructs a type whose keys are picked from the given type */ - public Pick)[]>(schema: T, keys: readonly [...K], options?: SchemaOptions): TPick - /** `[Json]` Constructs a type whose keys are picked from the given type */ - public Pick[]>>(schema: T, keys: K, options?: SchemaOptions): TPick[number]> - /** `[Json]` Constructs a type whose keys are picked from the given type */ - public Pick>(schema: T, key: K, options?: SchemaOptions): TPick - /** `[Json]` Constructs a type whose keys are picked from the given type */ - public Pick(schema: T, key: K, options?: SchemaOptions): TPick[number]> - /** `[Json]` Constructs a type whose keys are picked from the given type */ - public Pick(schema: T, key: K, options?: SchemaOptions): TPick - /** `[Json]` Constructs a type whose keys are picked from the given type */ - public Pick(schema: TSchema, unresolved: any, options: SchemaOptions = {}): any { - const keys = KeyArrayResolver.Resolve(unresolved) - // prettier-ignore - return ObjectMap.Map(this.Discard(TypeClone.Type(schema), ['$id', Transform]), (object) => { - if (ValueGuard.IsArray(object.required)) { - object.required = object.required.filter((key: any) => keys.includes(key)) - if (object.required.length === 0) delete object.required - } - for (const key of Object.getOwnPropertyNames(object.properties)) { - if (!keys.includes(key as any)) delete object.properties[key] - } - return this.Create(object) - }, options) - } - /** `[Json]` Creates a Record type */ - public Record(key: K, schema: T, options: ObjectOptions = {}): TRecordResolve { - // prettier-ignore - return ( - TypeGuard.TTemplateLiteral(key) ? (() => { - const expression = TemplateLiteralParser.ParseExact(key.pattern) - // prettier-ignore - return TemplateLiteralFinite.Check(expression) - ? (this.Object([...TemplateLiteralGenerator.Generate(expression)].reduce((acc, key) => ({ ...acc, [key]: TypeClone.Type(schema) }), {} as TProperties), options)) - : this.Create({ ...options, [Kind]: 'Record', type: 'object', patternProperties: { [key.pattern]: TypeClone.Type(schema) }}) - })() : - TypeGuard.TUnion(key) ? (() => { - const union = UnionResolver.Resolve(key) - if (TypeGuard.TUnionLiteral(union)) { - const properties = union.anyOf.reduce((acc: any, literal: any) => ({ ...acc, [literal.const]: TypeClone.Type(schema) }), {} as TProperties) - return this.Object(properties, { ...options, [Hint]: 'Record' }) - } else this.Throw('Record key of type union contains non-literal types') - })() : - TypeGuard.TLiteral(key) ? (() => { - // prettier-ignore - return (ValueGuard.IsString(key.const) || ValueGuard.IsNumber(key.const)) - ? this.Object({ [key.const]: TypeClone.Type(schema) }, options) - : this.Throw('Record key of type literal is not of type string or number') - })() : - TypeGuard.TInteger(key) || TypeGuard.TNumber(key) ? (() => { - return this.Create({ ...options, [Kind]: 'Record', type: 'object', patternProperties: { [PatternNumberExact]: TypeClone.Type(schema) } }) - })() : - TypeGuard.TString(key) ? (() => { - const pattern = ValueGuard.IsUndefined(key.pattern) ? PatternStringExact : key.pattern - return this.Create({ ...options, [Kind]: 'Record', type: 'object', patternProperties: { [pattern]: TypeClone.Type(schema) } }) - })() : - this.Never() - ) - } - /** `[Json]` Creates a Recursive type */ - public Recursive(callback: (thisType: TThis) => T, options: SchemaOptions = {}): TRecursive { - if (ValueGuard.IsUndefined(options.$id)) (options as any).$id = `T${TypeOrdinal++}` - const thisType = callback({ [Kind]: 'This', $ref: `${options.$id}` } as any) - thisType.$id = options.$id - return this.Create({ ...options, [Hint]: 'Recursive', ...thisType } as any) - } - /** `[Json]` Creates a Ref type. The referenced type must contain a $id */ - public Ref(schema: T, options?: SchemaOptions): TRef - /** `[Json]` Creates a Ref type. */ - public Ref($ref: string, options?: SchemaOptions): TRef - /** `[Json]` Creates a Ref type. */ - public Ref(unresolved: TSchema | string, options: SchemaOptions = {}) { - if (ValueGuard.IsString(unresolved)) return this.Create({ ...options, [Kind]: 'Ref', $ref: unresolved }) - if (ValueGuard.IsUndefined(unresolved.$id)) this.Throw('Reference target type must specify an $id') - return this.Create({ ...options, [Kind]: 'Ref', $ref: unresolved.$id! }) - } - /** `[Json]` Constructs a type where all properties are required */ - public Required(schema: T, options: SchemaOptions = {}): TRequired { - // prettier-ignore - return ObjectMap.Map(this.Discard(TypeClone.Type(schema), ['$id', Transform]), (object) => { - const properties = Object.getOwnPropertyNames(object.properties).reduce((acc, key) => { - return { ...acc, [key]: this.Discard(object.properties[key], [Optional]) as TSchema } - }, {} as TProperties) - return this.Object(properties, object /* object used as options to retain other constraints */) - }, options) - } - /** `[Json]` Extracts interior Rest elements from Tuple, Intersect and Union types */ - public Rest(schema: T): TRest { - return ( - TypeGuard.TTuple(schema) && !ValueGuard.IsUndefined(schema.items) ? TypeClone.Rest(schema.items) : TypeGuard.TIntersect(schema) ? TypeClone.Rest(schema.allOf) : TypeGuard.TUnion(schema) ? TypeClone.Rest(schema.anyOf) : [] - ) as TRest - } - /** `[Json]` Creates a String type */ - public String(options: StringOptions = {}): TString { - return this.Create({ ...options, [Kind]: 'String', type: 'string' }) - } - /** `[Json]` Creates a TemplateLiteral type from template dsl string */ - public TemplateLiteral(templateDsl: T, options?: SchemaOptions): TTemplateLiteralDslParser - /** `[Json]` Creates a TemplateLiteral type */ - public TemplateLiteral(kinds: [...T], options?: SchemaOptions): TTemplateLiteral - /** `[Json]` Creates a TemplateLiteral type */ - public TemplateLiteral(unresolved: unknown, options: SchemaOptions = {}) { - // prettier-ignore - const pattern = ValueGuard.IsString(unresolved) - ? TemplateLiteralPattern.Create(TemplateLiteralDslParser.Parse(unresolved)) - : TemplateLiteralPattern.Create(unresolved as TTemplateLiteralKind[]) - return this.Create({ ...options, [Kind]: 'TemplateLiteral', type: 'string', pattern }) - } - /** `[Json]` Creates a Transform type */ - public Transform(schema: I): TransformDecodeBuilder { - return new TransformDecodeBuilder(schema) - } - /** `[Json]` Creates a Tuple type */ - public Tuple(items: [...T], options: SchemaOptions = {}): TTuple { - const [additionalItems, minItems, maxItems] = [false, items.length, items.length] - const clonedItems = TypeClone.Rest(items) - // prettier-ignore - const schema = (items.length > 0 ? - { ...options, [Kind]: 'Tuple', type: 'array', items: clonedItems, additionalItems, minItems, maxItems } : - { ...options, [Kind]: 'Tuple', type: 'array', minItems, maxItems }) as any - return this.Create(schema) - } - /** `[Json]` Intrinsic function to Uncapitalize LiteralString types */ - public Uncapitalize(schema: T, options: SchemaOptions = {}): TIntrinsic { - return { ...Intrinsic.Map(TypeClone.Type(schema), 'Uncapitalize'), ...options } - } - /** `[Json]` Creates a Union type */ - public Union(anyOf: [], options?: SchemaOptions): TNever - /** `[Json]` Creates a Union type */ - public Union(anyOf: [...T], options?: SchemaOptions): T[0] - /** `[Json]` Creates a Union type */ - public Union(anyOf: [...T], options?: SchemaOptions): TUnion - /** `[Json-Experimental]` Converts a TemplateLiteral into a Union */ - public Union(template: T): TUnionTemplateLiteral - /** `[Json]` Creates a Union type */ - public Union(union: TSchema[] | TTemplateLiteral, options: SchemaOptions = {}) { - // prettier-ignore - return TypeGuard.TTemplateLiteral(union) - ? TemplateLiteralResolver.Resolve(union) - : (() => { - const anyOf = union - if (anyOf.length === 0) return this.Never(options) - if (anyOf.length === 1) return this.Create(TypeClone.Type(anyOf[0], options)) - const clonedAnyOf = TypeClone.Rest(anyOf) - return this.Create({ ...options, [Kind]: 'Union', anyOf: clonedAnyOf }) - })() - } - /** `[Json]` Creates an Unknown type */ - public Unknown(options: SchemaOptions = {}): TUnknown { - return this.Create({ ...options, [Kind]: 'Unknown' }) - } - /** `[Json]` Creates a Unsafe type that will infers as the generic argument T */ - public Unsafe(options: UnsafeOptions = {}): TUnsafe { - return this.Create({ ...options, [Kind]: options[Kind] || 'Unsafe' }) - } - /** `[Json]` Intrinsic function to Uppercase LiteralString types */ - public Uppercase(schema: T, options: SchemaOptions = {}): TIntrinsic { - return { ...Intrinsic.Map(TypeClone.Type(schema), 'Uppercase'), ...options } - } -} -// -------------------------------------------------------------------------- -// JavaScriptTypeBuilder -// -------------------------------------------------------------------------- -export class JavaScriptTypeBuilder extends JsonTypeBuilder { - /** `[JavaScript]` Creates a AsyncIterator type */ - public AsyncIterator(items: T, options: SchemaOptions = {}): TAsyncIterator { - return this.Create({ ...options, [Kind]: 'AsyncIterator', type: 'AsyncIterator', items: TypeClone.Type(items) }) - } - /** `[JavaScript]` Constructs a type by recursively unwrapping Promise types */ - public Awaited(schema: T, options: SchemaOptions = {}): TAwaited { - // prettier-ignore - const Unwrap = (rest: TSchema[]): TSchema[] => rest.length > 0 ? (() => { - const [L, ...R] = rest - return [this.Awaited(L), ...Unwrap(R)] - })() : rest - // prettier-ignore - return ( - TypeGuard.TIntersect(schema) ? Type.Intersect(Unwrap(schema.allOf)) : - TypeGuard.TUnion(schema) ? Type.Union(Unwrap(schema.anyOf)) : - TypeGuard.TPromise(schema) ? this.Awaited(schema.item) : - TypeClone.Type(schema, options) - ) as TAwaited - } - /** `[JavaScript]` Creates a BigInt type */ - public BigInt(options: NumericOptions = {}): TBigInt { - return this.Create({ ...options, [Kind]: 'BigInt', type: 'bigint' }) - } - /** `[JavaScript]` Extracts the ConstructorParameters from the given Constructor type */ - public ConstructorParameters>(schema: T, options: SchemaOptions = {}): TConstructorParameters { - return this.Tuple([...schema.parameters], { ...options }) - } - /** `[JavaScript]` Creates a Constructor type */ - public Constructor(parameters: [...T], returns: U, options?: SchemaOptions): TConstructor { - const [clonedParameters, clonedReturns] = [TypeClone.Rest(parameters), TypeClone.Type(returns)] - return this.Create({ ...options, [Kind]: 'Constructor', type: 'Constructor', parameters: clonedParameters, returns: clonedReturns }) - } - /** `[JavaScript]` Creates a Date type */ - public Date(options: DateOptions = {}): TDate { - return this.Create({ ...options, [Kind]: 'Date', type: 'Date' }) - } - /** `[JavaScript]` Creates a Function type */ - public Function(parameters: [...T], returns: U, options?: SchemaOptions): TFunction { - const [clonedParameters, clonedReturns] = [TypeClone.Rest(parameters), TypeClone.Type(returns)] - return this.Create({ ...options, [Kind]: 'Function', type: 'Function', parameters: clonedParameters, returns: clonedReturns }) - } - /** `[JavaScript]` Extracts the InstanceType from the given Constructor type */ - public InstanceType>(schema: T, options: SchemaOptions = {}): TInstanceType { - return TypeClone.Type(schema.returns, options) - } - /** `[JavaScript]` Creates an Iterator type */ - public Iterator(items: T, options: SchemaOptions = {}): TIterator { - return this.Create({ ...options, [Kind]: 'Iterator', type: 'Iterator', items: TypeClone.Type(items) }) - } - /** `[JavaScript]` Extracts the Parameters from the given Function type */ - public Parameters>(schema: T, options: SchemaOptions = {}): TParameters { - return this.Tuple(schema.parameters, { ...options }) - } - /** `[JavaScript]` Creates a Promise type */ - public Promise(item: T, options: SchemaOptions = {}): TPromise { - return this.Create({ ...options, [Kind]: 'Promise', type: 'Promise', item: TypeClone.Type(item) }) - } - /** `[JavaScript]` Creates a String type from a Regular Expression pattern */ - public RegExp(pattern: string, options?: SchemaOptions): TString - /** `[JavaScript]` Creates a String type from a Regular Expression */ - public RegExp(regex: RegExp, options?: SchemaOptions): TString - /** `[Extended]` Creates a String type */ - public RegExp(unresolved: string | RegExp, options: SchemaOptions = {}) { - const pattern = ValueGuard.IsString(unresolved) ? unresolved : unresolved.source - return this.Create({ ...options, [Kind]: 'String', type: 'string', pattern }) - } - /** - * @deprecated Use `Type.RegExp` - */ - public RegEx(regex: RegExp, options: SchemaOptions = {}): TString { - return this.RegExp(regex, options) - } - /** `[JavaScript]` Extracts the ReturnType from the given Function type */ - public ReturnType>(schema: T, options: SchemaOptions = {}): TReturnType { - return TypeClone.Type(schema.returns, options) - } - /** `[JavaScript]` Creates a Symbol type */ - public Symbol(options?: SchemaOptions): TSymbol { - return this.Create({ ...options, [Kind]: 'Symbol', type: 'symbol' }) - } - /** `[JavaScript]` Creates a Undefined type */ - public Undefined(options: SchemaOptions = {}): TUndefined { - return this.Create({ ...options, [Kind]: 'Undefined', type: 'undefined' }) - } - /** `[JavaScript]` Creates a Uint8Array type */ - public Uint8Array(options: Uint8ArrayOptions = {}): TUint8Array { - return this.Create({ ...options, [Kind]: 'Uint8Array', type: 'Uint8Array' }) - } - /** `[JavaScript]` Creates a Void type */ - public Void(options: SchemaOptions = {}): TVoid { - return this.Create({ ...options, [Kind]: 'Void', type: 'void' }) - } -} -/** Json Type Builder with Static Resolution for TypeScript */ -export const JsonType = new JsonTypeBuilder() -/** JavaScript Type Builder with Static Resolution for TypeScript */ -export const Type = new JavaScriptTypeBuilder() diff --git a/src/value/cast.ts b/src/value/cast/cast.ts similarity index 67% rename from src/value/cast.ts rename to src/value/cast/cast.ts index 16e3ec9cf..d75ca1b62 100644 --- a/src/value/cast.ts +++ b/src/value/cast/cast.ts @@ -26,52 +26,66 @@ 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 { 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) { +// ------------------------------------------------------------------ +export class ValueCastArrayUniqueItemsTypeError extends Error { + constructor(public readonly schema: TSchema, public readonly value: unknown) { super('Array cast produced invalid data due to uniqueItems constraint') } } -export class ValueCastNeverTypeError extends Types.TypeBoxError { - constructor(public readonly schema: Types.TSchema) { +export class ValueCastNeverTypeError extends Error { + constructor(public readonly schema: TSchema) { super('Never types cannot be cast') } } -export class ValueCastRecursiveTypeError extends Types.TypeBoxError { - constructor(public readonly schema: Types.TSchema) { +export class ValueCastRecursiveTypeError extends Error { + constructor(public readonly schema: TSchema) { super('Cannot cast recursive schemas') } } -export class ValueCastUnknownTypeError extends Types.TypeBoxError { - constructor(public readonly schema: Types.TSchema) { +export class ValueCastUnknownTypeError extends Error { + constructor(public readonly schema: 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. -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ +// 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 + function Score(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[Types.Kind] === 'Literal' && schema.const === value[key] ? max : 0 + 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) @@ -80,7 +94,7 @@ namespace UnionCastCreate { return Check(schema, references, value) ? 1 : 0 } } - function Select(union: Types.TUnion, references: Types.TSchema[], value: any): Types.TSchema { + function Select(union: TUnion, references: TSchema[], value: any): TSchema { let [select, best] = [union.anyOf[0], 0] for (const schema of union.anyOf) { const score = Score(schema, references, value) @@ -91,7 +105,7 @@ namespace UnionCastCreate { } return select } - export function Create(union: Types.TUnion, references: Types.TSchema[], value: any) { + export function Create(union: TUnion, references: TSchema[], value: any) { if ('default' in union) { return union.default } else { @@ -100,19 +114,19 @@ namespace UnionCastCreate { } } } -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ // 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 TArray(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 @@ -123,25 +137,25 @@ function TArray(schema: Types.TArray, references: Types.TSchema[], value: any): if (!Check(schema, references, unique)) throw new ValueCastArrayUniqueItemsTypeError(schema, unique) return unique } -function TConstructor(schema: Types.TConstructor, references: Types.TSchema[], value: any): any { +function TConstructor(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 TIntersect(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 { +function TNever(schema: TNever, references: TSchema[], value: any): any { throw new ValueCastNeverTypeError(schema) } -function TObject(schema: Types.TObject, references: Types.TSchema[], value: any): any { +function TObject(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 +174,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 TRecord(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,28 +185,28 @@ function TRecord(schema: Types.TRecord, references: Types.TSchema[], v } return result } -function TRef(schema: Types.TRef, references: Types.TSchema[], value: any): any { +function TRef(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 TThis(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 TTuple(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 { +function TUnion(schema: TUnion, references: TSchema[], value: any): any { return Check(schema, references, value) ? Clone(value) : UnionCastCreate.Create(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) case 'Constructor': @@ -213,46 +227,27 @@ function Visit(schema: Types.TSchema, references: Types.TSchema[], value: any): return TTuple(schema_, references_, value) case 'Union': return TUnion(schema_, references_, value) - // ------------------------------------------------------ + // -------------------------------------------------------------- // DefaultClone - // ------------------------------------------------------ + // -------------------------------------------------------------- case 'Date': case 'Symbol': case 'Uint8Array': return DefaultClone(schema, references, value) - // ------------------------------------------------------ + // -------------------------------------------------------------- // Default - // ------------------------------------------------------ - case 'Any': - case 'AsyncIterator': - case 'BigInt': - case 'Boolean': - case 'Function': - case 'Integer': - case 'Iterator': - case 'Literal': - case 'Not': - case 'Null': - case 'Number': - case 'Promise': - case 'String': - case 'TemplateLiteral': - case 'Undefined': - case 'Unknown': - case 'Void': - return Default(schema_, references_, value) + // -------------------------------------------------------------- default: - if (!Types.TypeRegistry.Has(schema_[Types.Kind])) throw new ValueCastUnknownTypeError(schema_) return Default(schema_, references_, value) } } -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ // Cast -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ /** Casts a value into a given type and references. The return value will retain as much information of the original value as possible. */ -export function Cast(schema: T, references: Types.TSchema[], value: unknown): Types.Static +export function Cast(schema: T, references: TSchema[], value: unknown): Static /** Casts a value into a given type. The return value will retain as much information of the original value as possible. */ -export function Cast(schema: T, value: unknown): Types.Static +export function Cast(schema: T, value: unknown): Static /** Casts a value into a given type. The return value will retain as much information of the original value as possible. */ export function Cast(...args: any[]) { return args.length === 3 ? Visit(args[0], args[1], args[2]) : Visit(args[0], [], args[1]) diff --git a/src/value/cast/index.ts b/src/value/cast/index.ts new file mode 100644 index 000000000..5d403f74a --- /dev/null +++ b/src/value/cast/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './cast' diff --git a/src/value/check.ts b/src/value/check/check.ts similarity index 69% rename from src/value/check.ts rename to src/value/check/check.ts index 7e987708c..96ae115b7 100644 --- a/src/value/check.ts +++ b/src/value/check/check.ts @@ -26,25 +26,62 @@ 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 { IsArray, IsUint8Array, IsDate, IsPromise, IsFunction, IsAsyncIterator, IsIterator, IsBoolean, IsNumber, IsBigInt, IsString, IsSymbol, IsInteger, IsNull, IsUndefined } from '../guard/index' +import { TypeSystemPolicy } from '../../system/index' +import { Deref } from '../deref/index' +import { Hash } from '../hash/index' +import { Kind } from '../../type/symbols/index' +import { KeyOfStringResolvePattern } from '../../type/keyof/index' +import { ExtendsUndefinedCheck } from '../../type/extends/index' +import { TSchema as IsSchemaType } from '../../type/guard/type' +import { TypeRegistry, FormatRegistry } from '../../type/registry/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 { TTemplateLiteral, TTemplateLiteralKind } 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 ValueCheckUnknownTypeError extends Types.TypeBoxError { - constructor(public readonly schema: Types.TSchema) { +export class ValueCheckUnknownTypeError extends Error { + 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 @@ -55,10 +92,10 @@ function IsDefined(value: unknown): value is T { // -------------------------------------------------------------------------- // Types // -------------------------------------------------------------------------- -function TAny(schema: Types.TAny, references: Types.TSchema[], value: any): boolean { +function TAny(schema: TAny, references: TSchema[], value: any): boolean { return true } -function TArray(schema: Types.TArray, references: Types.TSchema[], value: any): boolean { +function TArray(schema: TArray, references: TSchema[], value: any): boolean { if (!IsArray(value)) return false if (IsDefined(schema.minItems) && !(value.length >= schema.minItems)) { return false @@ -77,7 +114,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 +127,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 TAsyncIterator(schema: TAsyncIterator, references: TSchema[], value: any): boolean { return IsAsyncIterator(value) } -function TBigInt(schema: Types.TBigInt, references: Types.TSchema[], value: any): boolean { +function TBigInt(schema: TBigInt, references: TSchema[], value: any): boolean { if (!IsBigInt(value)) return false if (IsDefined(schema.exclusiveMaximum) && !(value < schema.exclusiveMaximum)) { return false @@ -112,13 +149,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 TBoolean(schema: TBoolean, references: TSchema[], value: any): boolean { return IsBoolean(value) } -function TConstructor(schema: Types.TConstructor, references: Types.TSchema[], value: any): boolean { +function TConstructor(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 TDate(schema: TDate, references: TSchema[], value: any): boolean { if (!IsDate(value)) return false if (IsDefined(schema.exclusiveMaximumTimestamp) && !(value.getTime() < schema.exclusiveMaximumTimestamp)) { return false @@ -137,10 +174,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 TFunction(schema: TFunction, references: TSchema[], value: any): boolean { return IsFunction(value) } -function TInteger(schema: Types.TInteger, references: Types.TSchema[], value: any): boolean { +function TInteger(schema: TInteger, references: TSchema[], value: any): boolean { if (!IsInteger(value)) { return false } @@ -161,36 +198,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 TIntersect(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(KeyOfStringResolvePattern(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 (IsSchemaType(schema.unevaluatedProperties)) { + const keyCheck = new RegExp(KeyOfStringResolvePattern(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 TIterator(schema: TIterator, references: TSchema[], value: any): boolean { return IsIterator(value) } -function TLiteral(schema: Types.TLiteral, references: Types.TSchema[], value: any): boolean { +function TLiteral(schema: TLiteral, references: TSchema[], value: any): boolean { return value === schema.const } -function TNever(schema: Types.TNever, references: Types.TSchema[], value: any): boolean { +function TNever(schema: TNever, references: TSchema[], value: any): boolean { return false } -function TNot(schema: Types.TNot, references: Types.TSchema[], value: any): boolean { +function TNot(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 TNull(schema: TNull, references: TSchema[], value: any): boolean { return IsNull(value) } -function TNumber(schema: Types.TNumber, references: Types.TSchema[], value: any): boolean { +function TNumber(schema: TNumber, references: TSchema[], value: any): boolean { if (!TypeSystemPolicy.IsNumberLike(value)) return false if (IsDefined(schema.exclusiveMaximum) && !(value < schema.exclusiveMaximum)) { return false @@ -209,7 +246,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 TObject(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 +261,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 +280,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 TPromise(schema: TPromise, references: TSchema[], value: any): boolean { return IsPromise(value) } -function TRecord(schema: Types.TRecord, references: Types.TSchema[], value: any): boolean { +function TRecord(schema: TRecord, references: TSchema[], value: any): boolean { if (!TypeSystemPolicy.IsRecordLike(value)) { return false } @@ -269,7 +306,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 +316,10 @@ 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 TRef(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 TString(schema: TString, references: TSchema[], value: any): boolean { if (!IsString(value)) { return false } @@ -297,22 +334,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 TSymbol(schema: TSymbol, references: TSchema[], value: any): boolean { return IsSymbol(value) } -function TTemplateLiteral(schema: Types.TTemplateLiteralKind, references: Types.TSchema[], value: any): boolean { +function TTemplateLiteral(schema: TTemplateLiteralKind, 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 TThis(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 TTuple(schema: TTuple, references: TSchema[], value: any): boolean { if (!IsArray(value)) { return false } @@ -330,13 +367,13 @@ function TTuple(schema: Types.TTuple, references: Types.TSchema[], value: } return true } -function TUndefined(schema: Types.TUndefined, references: Types.TSchema[], value: any): boolean { +function TUndefined(schema: TUndefined, references: TSchema[], value: any): boolean { return IsUndefined(value) } -function TUnion(schema: Types.TUnion, references: Types.TSchema[], value: any): boolean { +function TUnion(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 TUint8Array(schema: TUint8Array, references: TSchema[], value: any): boolean { if (!IsUint8Array(value)) { return false } @@ -348,21 +385,21 @@ function TUint8Array(schema: Types.TUint8Array, references: Types.TSchema[], val } return true } -function TUnknown(schema: Types.TUnknown, references: Types.TSchema[], value: any): boolean { +function TUnknown(schema: TUnknown, references: TSchema[], value: any): boolean { return true } -function TVoid(schema: Types.TVoid, references: Types.TSchema[], value: any): boolean { +function TVoid(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 TKind(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) case 'Array': @@ -424,7 +461,7 @@ function Visit(schema: T, references: Types.TSchema[], case 'Void': return TVoid(schema_, references_, value) default: - if (!Types.TypeRegistry.Has(schema_[Types.Kind])) throw new ValueCheckUnknownTypeError(schema_) + if (!TypeRegistry.Has(schema_[Kind])) throw new ValueCheckUnknownTypeError(schema_) return TKind(schema_, references_, value) } } @@ -432,9 +469,9 @@ function Visit(schema: T, references: Types.TSchema[], // Check // -------------------------------------------------------------------------- /** Returns true if the value matches the given type. */ -export function Check(schema: T, references: Types.TSchema[], value: unknown): value is Types.Static +export function Check(schema: T, references: TSchema[], value: unknown): value is Static /** Returns true if the value matches the given type. */ -export function Check(schema: T, value: unknown): value is Types.Static +export function Check(schema: T, value: unknown): value is Static /** Returns true if the value matches the given type. */ export function Check(...args: any[]) { return args.length === 3 ? Visit(args[0], args[1], args[2]) : Visit(args[0], [], args[1]) diff --git a/src/value/check/index.ts b/src/value/check/index.ts new file mode 100644 index 000000000..e318a6581 --- /dev/null +++ b/src/value/check/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './check' diff --git a/src/value/clean/clean.ts b/src/value/clean/clean.ts new file mode 100644 index 000000000..2895fde19 --- /dev/null +++ b/src/value/clean/clean.ts @@ -0,0 +1,175 @@ +/*-------------------------------------------------------------------------- + +@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, IsObject, IsArray, IsUndefined } from '../guard/index' +import { TSchema as IsSchemaType } from '../../type/guard/type' +import { KeyOfStringResolve } from '../../type/keyof/keyof-string' +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' + +// ------------------------------------------------------------------ +// IsSchema +// ------------------------------------------------------------------ +function IsSchema(schema: unknown): schema is TSchema { + return IsSchemaType(schema) +} +// ------------------------------------------------------------------ +// IsCheckable +// ------------------------------------------------------------------ +function IsCheckable(schema: unknown): boolean { + return IsSchemaType(schema) && schema[Kind] !== 'Unsafe' +} +// ------------------------------------------------------------------ +// Types +// ------------------------------------------------------------------ +function TArray(schema: TArray, references: TSchema[], value: unknown): any { + if (!IsArray(value)) return value + return value.map((value) => Visit(schema.items, references, value)) +} +function TIntersect(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 = KeyOfStringResolve(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 TObject(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 TRecord(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 TRef(schema: TRef, references: TSchema[], value: unknown): any { + return Visit(Deref(schema, references), references, value) +} +function TThis(schema: TThis, references: TSchema[], value: unknown): any { + return Visit(Deref(schema, references), references, value) +} +function TTuple(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 TUnion(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 TArray(schema_, references_, value) + case 'Intersect': + return TIntersect(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 value + } +} +// ------------------------------------------------------------------ +// Clean +// ------------------------------------------------------------------ +/** `[Mutable]` Removes excess properties from a value and returns the result. This function does not check the value and returns an unknown type. You should Check the result before use. Clean is a mutable operation. To avoid mutation, Clone the value first. */ +export function Clean(schema: T, references: TSchema[], value: unknown): unknown +/** `[Mutable]` Removes excess properties from a value and returns the result. This function does not check the value and returns an unknown type. You should Check the result before use. Clean is a mutable operation. To avoid mutation, Clone the value first. */ +export function Clean(schema: T): unknown +/** `[Mutable]` Removes excess properties from a value and returns the result. This function does not check the value and returns an unknown type. You should Check the result before use. Clean is a mutable operation. To avoid mutation, Clone the value first. */ +export function Clean(...args: any[]) { + return args.length === 3 ? Visit(args[0], args[1], args[2]) : Visit(args[0], [], args[1]) +} diff --git a/src/value/clean/index.ts b/src/value/clean/index.ts new file mode 100644 index 000000000..4e55b279e --- /dev/null +++ b/src/value/clean/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './clean' diff --git a/src/value/clone.ts b/src/value/clone/clone.ts similarity index 98% rename from src/value/clone.ts rename to src/value/clone/clone.ts index a2e2d1f30..aad338c61 100644 --- a/src/value/clone.ts +++ b/src/value/clone/clone.ts @@ -26,8 +26,8 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { IsArray, IsDate, IsPlainObject, IsTypedArray, IsValueType } from './guard' -import type { ObjectType, ArrayType, TypedArrayType, ValueType } from './guard' +import { IsArray, IsDate, IsPlainObject, IsTypedArray, IsValueType } from '../guard/index' +import type { ObjectType, ArrayType, TypedArrayType, ValueType } from '../guard/index' // -------------------------------------------------------------------------- // Clonable diff --git a/src/value/clone/index.ts b/src/value/clone/index.ts new file mode 100644 index 000000000..4294f9957 --- /dev/null +++ b/src/value/clone/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './clone' diff --git a/src/value/convert.ts b/src/value/convert/convert.ts similarity index 60% rename from src/value/convert.ts rename to src/value/convert/convert.ts index 60ba2e1f3..2f6d2bcf9 100644 --- a/src/value/convert.ts +++ b/src/value/convert/convert.ts @@ -26,23 +26,46 @@ 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 { IsArray, IsObject, IsDate, IsUndefined, IsString, IsNumber, IsBoolean, IsBigInt, IsSymbol } from '../guard/index' +import { Clone } from '../clone/index' +import { Check } from '../check/index' +import { Deref } from '../deref/index' -// -------------------------------------------------------------------------- +import { TObject 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' + +// ------------------------------------------------------------------ // Errors -// -------------------------------------------------------------------------- -export class ValueConvertUnknownTypeError extends Types.TypeBoxError { - constructor(public readonly schema: Types.TSchema) { +// ------------------------------------------------------------------ +export class ValueConvertUnknownTypeError extends Error { + constructor(public readonly schema: TSchema) { super('Unknown type') } } -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ // Conversions -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ function IsStringNumeric(value: unknown): value is string { return IsString(value) && !isNaN(value as any) && !isNaN(parseFloat(value)) } @@ -70,9 +93,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 +108,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 +138,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 TArray(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 TBigInt(schema: TBigInt, references: TSchema[], value: any): unknown { return TryConvertBigInt(value) } -function TBoolean(schema: Types.TBoolean, references: Types.TSchema[], value: any): unknown { +function TBoolean(schema: TBoolean, references: TSchema[], value: any): unknown { return TryConvertBoolean(value) } -function TDate(schema: Types.TDate, references: Types.TSchema[], value: any): unknown { +function TDate(schema: TDate, references: TSchema[], value: any): unknown { return TryConvertDate(value) } -function TInteger(schema: Types.TInteger, references: Types.TSchema[], value: any): unknown { +function TInteger(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 TIntersect(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 TLiteral(schema: TLiteral, references: TSchema[], value: any): unknown { return TryConvertLiteral(schema, value) } -function TNull(schema: Types.TNull, references: Types.TSchema[], value: any): unknown { +function TNull(schema: TNull, references: TSchema[], value: any): unknown { return TryConvertNull(value) } -function TNumber(schema: Types.TNumber, references: Types.TSchema[], value: any): unknown { +function TNumber(schema: TNumber, references: TSchema[], value: any): unknown { return TryConvertNumber(value) } -function TObject(schema: Types.TObject, references: Types.TSchema[], value: any): unknown { - if (IsObject(value)) - return Object.getOwnPropertyNames(schema.properties).reduce((acc, key) => { - return value[key] !== undefined ? { ...acc, [key]: Visit(schema.properties[key], references, value[key]) } : { ...acc } - }, value) - return value -} -function TRecord(schema: Types.TRecord, references: Types.TSchema[], value: any): unknown { +// prettier-ignore +function TObject(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: TRecord, references: TSchema[], value: any): unknown { const propertyKey = Object.getOwnPropertyNames(schema.patternProperties)[0] const property = schema.patternProperties[propertyKey] const result = {} as Record @@ -204,30 +222,32 @@ function TRecord(schema: Types.TRecord, references: Types.TSchema[], v } return result } -function TRef(schema: Types.TRef, references: Types.TSchema[], value: any): unknown { +function TRef(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 TString(schema: TString, references: TSchema[], value: any): unknown { return TryConvertString(value) } -function TSymbol(schema: Types.TSymbol, references: Types.TSchema[], value: any): unknown { +function TSymbol(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 TThis(schema: TThis, references: TSchema[], value: any): unknown { return Visit(Deref(schema, references), references, value) } -function TTuple(schema: Types.TTuple, references: Types.TSchema[], value: any): unknown { - if (IsArray(value) && !IsUndefined(schema.items)) { - return value.map((value, index) => { - return index < schema.items!.length ? Visit(schema.items![index], references, value) : value - }) - } - return value -} -function TUndefined(schema: Types.TUndefined, references: Types.TSchema[], value: any): unknown { +// prettier-ignore +function TTuple(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: TUndefined, references: TSchema[], value: any): unknown { return TryConvertUndefined(value) } -function TUnion(schema: Types.TUnion, references: Types.TSchema[], value: any): unknown { +function TUnion(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,13 +256,10 @@ 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) case 'BigInt': @@ -279,34 +296,21 @@ function Visit(schema: Types.TSchema, references: Types.TSchema[], value: any): return TUndefined(schema_, references_, value) case 'Union': return TUnion(schema_, references_, value) - // ------------------------------------------------------ - // Default - // ------------------------------------------------------ - case 'Any': - case 'AsyncIterator': - case 'Constructor': - case 'Function': - case 'Iterator': - case 'Never': - case 'Promise': - case 'TemplateLiteral': - case 'Uint8Array': - case 'Unknown': - case 'Void': - return Default(value) default: - if (!Types.TypeRegistry.Has(schema_[Types.Kind])) throw new ValueConvertUnknownTypeError(schema_) return Default(value) } } -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ // Convert -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ /** Converts any type mismatched values to their target type if a reasonable conversion is possible. */ -export function Convert(schema: T, references: Types.TSchema[], value: unknown): unknown +export function Convert(schema: T, references: TSchema[], value: unknown): unknown /** Converts any type mismatched values to their target type if a reasonable conversion is possible. */ -export function Convert(schema: T, value: unknown): unknown +export function Convert(schema: T, value: unknown): unknown /** Converts any type mismatched values to their target type if a reasonable conversion is possible. */ +// prettier-ignore export function Convert(...args: any[]) { - return args.length === 3 ? Visit(args[0], args[1], args[2]) : Visit(args[0], [], args[1]) + return args.length === 3 + ? Visit(args[0], args[1], args[2]) + : Visit(args[0], [], args[1]) } diff --git a/src/value/convert/index.ts b/src/value/convert/index.ts new file mode 100644 index 000000000..0e642e6ac --- /dev/null +++ b/src/value/convert/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './convert' diff --git a/src/value/create.ts b/src/value/create/create.ts similarity index 65% rename from src/value/create.ts rename to src/value/create/create.ts index b36b42f41..fd573e75c 100644 --- a/src/value/create.ts +++ b/src/value/create/create.ts @@ -26,55 +26,91 @@ 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 { TemplateLiteralParseExact, IsTemplateLiteralFinite, TemplateLiteralGenerate } 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 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 { 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) { +// ------------------------------------------------------------------ +export class ValueCreateUnknownTypeError extends Error { + constructor(public readonly schema: TSchema) { super('Unknown type') } } -export class ValueCreateNeverTypeError extends Types.TypeBoxError { - constructor(public readonly schema: Types.TSchema) { +export class ValueCreateNeverTypeError extends Error { + constructor(public readonly schema: TSchema) { super('Never types cannot be created') } } -export class ValueCreateNotTypeError extends Types.TypeBoxError { - constructor(public readonly schema: Types.TSchema) { +export class ValueCreateNotTypeError extends Error { + constructor(public readonly schema: TSchema) { super('Not types must have a default value') } } -export class ValueCreateIntersectTypeError extends Types.TypeBoxError { - constructor(public readonly schema: Types.TSchema) { +export class ValueCreateIntersectTypeError extends Error { + constructor(public readonly schema: TSchema) { super('Intersect produced invalid value. Consider using a default value.') } } -export class ValueCreateTempateLiteralTypeError extends Types.TypeBoxError { - constructor(public readonly schema: Types.TSchema) { +export class ValueCreateTempateLiteralTypeError extends Error { + constructor(public readonly schema: 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) { +export class ValueCreateRecursiveInstantiationError extends Error { + constructor(public readonly schema: 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 TAny(schema: TAny, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { return schema.default } else { return {} } } -function TArray(schema: Types.TArray, references: Types.TSchema[]): any { +function TArray(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') } else if ('contains' in schema && !HasPropertyKey(schema, 'default')) { @@ -89,28 +125,28 @@ function TArray(schema: Types.TArray, references: Types.TSchema[]): any { return [] } } -function TAsyncIterator(schema: Types.TAsyncIterator, references: Types.TSchema[]) { +function TAsyncIterator(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 TBigInt(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 TBoolean(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 TConstructor(schema: TConstructor, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { return schema.default } else { @@ -129,7 +165,7 @@ function TConstructor(schema: Types.TConstructor, references: Types.TSchema[]): } } } -function TDate(schema: Types.TDate, references: Types.TSchema[]): any { +function TDate(schema: TDate, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { return schema.default } else if (schema.minimumTimestamp !== undefined) { @@ -138,14 +174,14 @@ function TDate(schema: Types.TDate, references: Types.TSchema[]): any { return new Date() } } -function TFunction(schema: Types.TFunction, references: Types.TSchema[]): any { +function TFunction(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 TInteger(schema: TInteger, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { return schema.default } else if (schema.minimum !== undefined) { @@ -154,13 +190,17 @@ function TInteger(schema: Types.TInteger, references: Types.TSchema[]): any { return 0 } } -function TIntersect(schema: Types.TIntersect, references: Types.TSchema[]): any { +function TIntersect(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 @@ -169,38 +209,38 @@ function TIntersect(schema: Types.TIntersect, references: Types.TSchema[]): any return value } } -function TIterator(schema: Types.TIterator, references: Types.TSchema[]) { +function TIterator(schema: TIterator, references: TSchema[]) { if (HasPropertyKey(schema, 'default')) { return schema.default } else { return (function* () {})() } } -function TLiteral(schema: Types.TLiteral, references: Types.TSchema[]): any { +function TLiteral(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 { +function TNever(schema: TNever, references: TSchema[]): any { throw new ValueCreateNeverTypeError(schema) } -function TNot(schema: Types.TNot, references: Types.TSchema[]): any { +function TNot(schema: TNot, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { return schema.default } else { throw new ValueCreateNotTypeError(schema) } } -function TNull(schema: Types.TNull, references: Types.TSchema[]): any { +function TNull(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 TNumber(schema: TNumber, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { return schema.default } else if (schema.minimum !== undefined) { @@ -209,7 +249,7 @@ function TNumber(schema: Types.TNumber, references: Types.TSchema[]): any { return 0 } } -function TObject(schema: Types.TObject, references: Types.TSchema[]): any { +function TObject(schema: TObject, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { return schema.default } else { @@ -222,18 +262,18 @@ function TObject(schema: Types.TObject, references: Types.TSchema[]): any { ) } } -function TPromise(schema: Types.TPromise, references: Types.TSchema[]): any { +function TPromise(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 TRecord(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,14 +282,14 @@ function TRecord(schema: Types.TRecord, references: Types.TSchema[]): return {} } } -function TRef(schema: Types.TRef, references: Types.TSchema[]): any { +function TRef(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 TString(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') @@ -274,7 +314,7 @@ function TString(schema: Types.TString, references: Types.TSchema[]): any { } } } -function TSymbol(schema: Types.TString, references: Types.TSchema[]): any { +function TSymbol(schema: TString, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { return schema.default } else if ('value' in schema) { @@ -283,16 +323,16 @@ function TSymbol(schema: Types.TString, references: Types.TSchema[]): any { return Symbol() } } -function TTemplateLiteral(schema: Types.TTemplateLiteral, references: Types.TSchema[]) { +function TTemplateLiteral(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) + const expression = TemplateLiteralParseExact(schema.pattern) + if (!IsTemplateLiteralFinite(expression)) throw new ValueCreateTempateLiteralTypeError(schema) + const sequence = TemplateLiteralGenerate(expression) return sequence.next().value } -function TThis(schema: Types.TThis, references: Types.TSchema[]): any { +function TThis(schema: TThis, references: TSchema[]): any { if (recursiveDepth++ > recursiveMaxDepth) throw new ValueCreateRecursiveInstantiationError(schema, recursiveMaxDepth) if (HasPropertyKey(schema, 'default')) { return schema.default @@ -300,7 +340,7 @@ function TThis(schema: Types.TThis, references: Types.TSchema[]): any { return Visit(Deref(schema, references), references) } } -function TTuple(schema: Types.TTuple, references: Types.TSchema[]): any { +function TTuple(schema: TTuple, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { return schema.default } @@ -310,14 +350,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 TUndefined(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 TUnion(schema: TUnion, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { return schema.default } else if (schema.anyOf.length === 0) { @@ -326,7 +366,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 TUint8Array(schema: TUint8Array, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { return schema.default } else if (schema.minByteLength !== undefined) { @@ -335,31 +375,31 @@ function TUint8Array(schema: Types.TUint8Array, references: Types.TSchema[]): an return new Uint8Array(0) } } -function TUnknown(schema: Types.TUnknown, references: Types.TSchema[]): any { +function TUnknown(schema: TUnknown, references: TSchema[]): any { if (HasPropertyKey(schema, 'default')) { return schema.default } else { return {} } } -function TVoid(schema: Types.TVoid, references: Types.TSchema[]): any { +function TVoid(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 TKind(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_) case 'Array': @@ -421,22 +461,22 @@ function Visit(schema: Types.TSchema, references: Types.TSchema[]): unknown { case 'Void': return TVoid(schema_, references_) default: - if (!Types.TypeRegistry.Has(schema_[Types.Kind])) throw new ValueCreateUnknownTypeError(schema_) + if (!TypeRegistry.Has(schema_[Kind])) throw new ValueCreateUnknownTypeError(schema_) return TKind(schema_, references_) } } -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ // State -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ const recursiveMaxDepth = 512 let recursiveDepth = 0 -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ // Create -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ /** Creates a value from the given schema and references */ -export function Create(schema: T, references: Types.TSchema[]): Types.Static +export function Create(schema: T, references: TSchema[]): Static /** Creates a value from the given schema */ -export function Create(schema: T): Types.Static +export function Create(schema: T): Static /** Creates a value from the given schema */ export function Create(...args: any[]) { recursiveDepth = 0 diff --git a/src/value/create/index.ts b/src/value/create/index.ts new file mode 100644 index 000000000..62543e72b --- /dev/null +++ b/src/value/create/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './create' diff --git a/src/value/default/default.ts b/src/value/default/default.ts new file mode 100644 index 000000000..852498e74 --- /dev/null +++ b/src/value/default/default.ts @@ -0,0 +1,179 @@ +/*-------------------------------------------------------------------------- + +@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, IsObject, IsArray, IsUndefined } from '../guard/index' +import { TSchema as IsSchemaType } from '../../type/guard/type' +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' + +// ------------------------------------------------------------------ +// ValueOrDefault +// ------------------------------------------------------------------ +function ValueOrDefault(schema: TSchema, value: unknown) { + return !(value === undefined) || !('default' in schema) ? value : schema.default +} +// ------------------------------------------------------------------ +// IsCheckable +// ------------------------------------------------------------------ +function IsCheckable(schema: unknown): boolean { + return IsSchemaType(schema) && schema[Kind] !== 'Unsafe' +} +// ------------------------------------------------------------------ +// IsDefaultSchema +// ------------------------------------------------------------------ +function IsDefaultSchema(value: unknown): value is TSchema { + return IsSchemaType(value) && 'default' in value +} +// ------------------------------------------------------------------ +// Types +// ------------------------------------------------------------------ +function TArray(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 TIntersect(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 TObject(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 TRecord(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 TRef(schema: TRef, references: TSchema[], value: unknown): any { + return Visit(Deref(schema, references), references, ValueOrDefault(schema, value)) +} +function TThis(schema: TThis, references: TSchema[], value: unknown): any { + return Visit(Deref(schema, references), references, value) +} +function TTuple(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 TUnion(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 TArray(schema_, references_, value) + case 'Intersect': + return TIntersect(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 ValueOrDefault(schema_, value) + } +} +// ------------------------------------------------------------------ +// Default +// ------------------------------------------------------------------ +/** `[Mutable]` Generates missing properties on a value using default schema annotations if available. This function does not check the value and returns an unknown type. You should Check the result before use. Default is a mutable operation. To avoid mutation, Clone the value first. */ +export function Default(schema: T, references: TSchema[], value: unknown): unknown +/** `[Mutable]` Generates missing properties on a value using default schema annotations if available. This function does not check the value and returns an unknown type. You should Check the result before use. Default is a mutable operation. To avoid mutation, Clone the value first. */ +export function Default(schema: T, value: unknown): unknown +/** `[Mutable]` Generates missing properties on a value using default schema annotations if available. This function does not check the value and returns an unknown type. You should Check the result before use. Default is a mutable operation. To avoid mutation, Clone the value first. */ +export function Default(...args: any[]) { + return args.length === 3 ? Visit(args[0], args[1], args[2]) : Visit(args[0], [], args[1]) +} diff --git a/src/value/default/index.ts b/src/value/default/index.ts new file mode 100644 index 000000000..fb74557fd --- /dev/null +++ b/src/value/default/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './default' diff --git a/src/value/delta.ts b/src/value/delta/delta.ts similarity index 79% rename from src/value/delta.ts rename to src/value/delta/delta.ts index 5a2092dc0..a87aaedbe 100644 --- a/src/value/delta.ts +++ b/src/value/delta/delta.ts @@ -26,37 +26,43 @@ 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 { 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') @@ -67,9 +73,9 @@ export class ValueDeltaUnableToDiffUnknownValue extends Error { super('Unable to create diff edits for unknown value') } } -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ // Command Factory -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ function CreateUpdate(path: string, value: unknown): Edit { return { type: 'update', path, value } } @@ -79,29 +85,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 (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]) + 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 (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 (IsUndefined(next[key]) && !nextKeys.includes(key)) yield CreateDelete(`${path}/${globalThis.String(key)}`) } } function* ArrayType(path: string, current: ArrayType, next: unknown): IterableIterator { @@ -119,7 +125,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]) } @@ -135,15 +141,15 @@ function* Visit(path: string, current: unknown, next: unknown): IterableIterator if (IsValueType(current)) return yield* ValueType(path, current, next) throw new ValueDeltaUnableToDiffUnknownValue(current) } -// --------------------------------------------------------------------- +// ------------------------------------------------------------------ // Diff -// --------------------------------------------------------------------- +// ------------------------------------------------------------------ export function Diff(current: unknown, next: unknown): Edit[] { return [...Visit('', current, next)] } -// --------------------------------------------------------------------- +// ------------------------------------------------------------------ // Patch -// --------------------------------------------------------------------- +// ------------------------------------------------------------------ function IsRootUpdate(edits: Edit[]): edits is [Update] { return edits.length > 0 && edits[0].path === '' && edits[0].type === 'update' } diff --git a/src/value/delta/index.ts b/src/value/delta/index.ts new file mode 100644 index 000000000..3eca3782f --- /dev/null +++ b/src/value/delta/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './delta' diff --git a/src/value/deref.ts b/src/value/deref/deref.ts similarity index 89% rename from src/value/deref.ts rename to src/value/deref/deref.ts index cf9ebd798..97f3bcf68 100644 --- a/src/value/deref.ts +++ b/src/value/deref/deref.ts @@ -26,9 +26,11 @@ 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' -export class TypeDereferenceError extends TypeBoxError { +export class TypeDereferenceError extends Error { constructor(public readonly schema: TRef | TThis) { super(`Unable to dereference schema with $id '${schema.$id}'`) } diff --git a/src/value/deref/index.ts b/src/value/deref/index.ts new file mode 100644 index 000000000..d0bf7e45b --- /dev/null +++ b/src/value/deref/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './deref' diff --git a/src/value/equal.ts b/src/value/equal/equal.ts similarity index 97% rename from src/value/equal.ts rename to src/value/equal/equal.ts index 558d96f8b..264093959 100644 --- a/src/value/equal.ts +++ b/src/value/equal/equal.ts @@ -26,12 +26,12 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -import { IsPlainObject, IsDate, IsArray, IsTypedArray, IsValueType } from './guard' -import type { ObjectType, ArrayType, TypedArrayType, ValueType } from './guard' +import { IsPlainObject, IsDate, IsArray, IsTypedArray, IsValueType } from '../guard/index' +import type { ObjectType, ArrayType, TypedArrayType, ValueType } from '../guard/index' -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ // Equality Checks -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ function ObjectType(left: ObjectType, right: unknown): boolean { if (!IsPlainObject(right)) return false const leftKeys = [...Object.keys(left), ...Object.getOwnPropertySymbols(left)] @@ -53,9 +53,9 @@ function TypedArrayType(left: TypedArrayType, right: unknown): any { function ValueType(left: ValueType, right: unknown): any { return left === right } -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ // Equal -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ /** Returns true if the left value deep-equals the right */ export function Equal(left: T, right: unknown): right is T { if (IsPlainObject(left)) return ObjectType(left, right) diff --git a/src/value/equal/index.ts b/src/value/equal/index.ts new file mode 100644 index 000000000..cf0e42f72 --- /dev/null +++ b/src/value/equal/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './equal' diff --git a/src/value/guard.ts b/src/value/guard/guard.ts similarity index 99% rename from src/value/guard.ts rename to src/value/guard/guard.ts index 5d5bb17ec..92aa187dd 100644 --- a/src/value/guard.ts +++ b/src/value/guard/guard.ts @@ -26,9 +26,9 @@ THE SOFTWARE. ---------------------------------------------------------------------------*/ -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ // Types -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ export type ObjectType = Record export type ArrayType = unknown[] export type ValueType = null | undefined | symbol | bigint | number | boolean | string diff --git a/src/value/guard/index.ts b/src/value/guard/index.ts new file mode 100644 index 000000000..36d7e3fa8 --- /dev/null +++ b/src/value/guard/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './guard' diff --git a/src/value/hash.ts b/src/value/hash/hash.ts similarity index 96% rename from src/value/hash.ts rename to src/value/hash/hash.ts index cd8935ead..e0f637751 100644 --- a/src/value/hash.ts +++ b/src/value/hash/hash.ts @@ -26,20 +26,20 @@ 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' -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ // Errors -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ export class ValueHashError extends Error { constructor(public readonly value: unknown) { super(`Unable to hash value`) } } -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ // ByteMarker -// -------------------------------------------------------------------------- -export enum ByteMarker { +// ------------------------------------------------------------------ +enum ByteMarker { Undefined, Null, Boolean, @@ -52,27 +52,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 +150,9 @@ function FNV1A64(byte: number) { Accumulator = Accumulator ^ Bytes[byte] Accumulator = (Accumulator * Prime) % Size } -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ // Hash -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ /** Creates a FNV1A-64 non cryptographic hash of the given value */ export function Hash(value: unknown) { Accumulator = BigInt('14695981039346656037') diff --git a/src/value/hash/index.ts b/src/value/hash/index.ts new file mode 100644 index 000000000..a2607845e --- /dev/null +++ b/src/value/hash/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './hash' diff --git a/src/value/index.ts b/src/value/index.ts index e8beb3bbd..aab2729a8 100644 --- a/src/value/index.ts +++ b/src/value/index.ts @@ -26,8 +26,58 @@ 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, ValueCastArrayUniqueItemsTypeError, ValueCastNeverTypeError, ValueCastRecursiveTypeError, ValueCastUnknownTypeError } from './cast/index' +export { Check } from './check/index' +export { Clean } from './clean/index' +export { Clone } from './clone/index' +export { Convert, ValueConvertUnknownTypeError } from './convert/index' +export { Create, ValueCreateIntersectTypeError, ValueCreateNeverTypeError, ValueCreateNotTypeError, ValueCreateRecursiveInstantiationError, ValueCreateTempateLiteralTypeError, ValueCreateUnknownTypeError } from './create/index' +export { Default } from './default/index' +export { Diff, Patch, Edit, Delete, Insert, Update, ValueDeltaObjectWithSymbolKeyError, ValueDeltaUnableToDiffUnknownValue } from './delta/index' +export { Equal } from './equal/index' +export { Hash, ValueHashError } from './hash/index' +export { Mutate, type Mutable, ValueMutateInvalidRootMutationError, ValueMutateTypeMismatchError } from './mutate/index' +export { ValuePointer } from './pointer/index' +export { Decode, Encode, 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, + ObjectType, + TypedArrayType, + ValueType, +} from './guard/index' +// ------------------------------------------------------------------ +// Value Namespace +// ------------------------------------------------------------------ +import { Value } from './value/index' +export { Value } +export default Value diff --git a/src/value/mutate/index.ts b/src/value/mutate/index.ts new file mode 100644 index 000000000..cf8bd9248 --- /dev/null +++ b/src/value/mutate/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './mutate' diff --git a/src/value/mutate.ts b/src/value/mutate/mutate.ts similarity index 93% rename from src/value/mutate.ts rename to src/value/mutate/mutate.ts index 42d563c4a..c1871402d 100644 --- a/src/value/mutate.ts +++ b/src/value/mutate/mutate.ts @@ -26,13 +26,13 @@ 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' -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ // Errors -// -------------------------------------------------------------------------- +// ------------------------------------------------------------------ export class ValueMutateTypeMismatchError extends Error { constructor() { super('Cannot assign due type mismatch of assignable values') @@ -43,9 +43,9 @@ export class ValueMutateInvalidRootMutationError extends Error { 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 +97,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,10 +110,10 @@ 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() diff --git a/src/value/pointer.ts b/src/value/pointer.ts deleted file mode 100644 index 995ce8129..000000000 --- a/src/value/pointer.ts +++ /dev/null @@ -1,121 +0,0 @@ -/*-------------------------------------------------------------------------- - -@sinclair/typebox/value - -The MIT License (MIT) - -Copyright (c) 2017-2023 Haydn Paterson (sinclair) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ----------------------------------------------------------------------------*/ - -// -------------------------------------------------------------------------- -// Errors -// -------------------------------------------------------------------------- -export class ValuePointerRootSetError extends Error { - constructor(public readonly value: unknown, public readonly path: string, public readonly update: unknown) { - super('Cannot set root value') - } -} -export class ValuePointerRootDeleteError extends Error { - constructor(public readonly value: unknown, public readonly path: string) { - super('Cannot delete root value') - } -} -// -------------------------------------------------------------------------- -// ValuePointer -// -------------------------------------------------------------------------- -/** Provides functionality to update values through RFC6901 string pointers */ -export namespace ValuePointer { - function Escape(component: string) { - return component.indexOf('~') === -1 ? component : component.replace(/~1/g, '/').replace(/~0/g, '~') - } - /** Formats the given pointer into navigable key components */ - export function* Format(pointer: string): IterableIterator { - if (pointer === '') return - let [start, end] = [0, 0] - for (let i = 0; i < pointer.length; i++) { - const char = pointer.charAt(i) - if (char === '/') { - if (i === 0) { - start = i + 1 - } else { - end = i - yield Escape(pointer.slice(start, end)) - start = i + 1 - } - } else { - end = i - } - } - yield Escape(pointer.slice(start)) - } - /** Sets the value at the given pointer. If the value at the pointer does not exist it is created */ - export function Set(value: any, pointer: string, update: unknown): void { - if (pointer === '') throw new ValuePointerRootSetError(value, pointer, update) - let [owner, next, key] = [null as any, value, ''] - for (const component of Format(pointer)) { - if (next[component] === undefined) next[component] = {} - owner = next - next = next[component] - key = component - } - owner[key] = update - } - /** Deletes a value at the given pointer */ - export function Delete(value: any, pointer: string): void { - if (pointer === '') throw new ValuePointerRootDeleteError(value, pointer) - let [owner, next, key] = [null as any, value as any, ''] - for (const component of Format(pointer)) { - if (next[component] === undefined || next[component] === null) return - owner = next - next = next[component] - key = component - } - if (Array.isArray(owner)) { - const index = parseInt(key) - owner.splice(index, 1) - } else { - delete owner[key] - } - } - /** Returns true if a value exists at the given pointer */ - export function Has(value: any, pointer: string): boolean { - if (pointer === '') return true - let [owner, next, key] = [null as any, value as any, ''] - for (const component of Format(pointer)) { - if (next[component] === undefined) return false - owner = next - next = next[component] - key = component - } - return Object.getOwnPropertyNames(owner).includes(key) - } - /** Gets the value at the given pointer */ - export function Get(value: any, pointer: string): any { - if (pointer === '') return value - let current = value - for (const component of Format(pointer)) { - if (current[component] === undefined) return undefined - current = current[component] - } - return current - } -} diff --git a/src/value/pointer/index.ts b/src/value/pointer/index.ts new file mode 100644 index 000000000..d4071966d --- /dev/null +++ b/src/value/pointer/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * as ValuePointer from './pointer' diff --git a/src/value/pointer/pointer.ts b/src/value/pointer/pointer.ts new file mode 100644 index 000000000..ff5db727d --- /dev/null +++ b/src/value/pointer/pointer.ts @@ -0,0 +1,125 @@ +/*-------------------------------------------------------------------------- + +@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 */ +// prettier-ignore +function Escape(component: string) { + return component.indexOf('~') === -1 ? component : component.replace(/~1/g, '/').replace(/~0/g, '~') +} +/** Formats the given pointer into navigable key components */ +// prettier-ignore +export function* Format(pointer: string): IterableIterator { + if (pointer === '') return + let [start, end] = [0, 0] + for (let i = 0; i < pointer.length; i++) { + const char = pointer.charAt(i) + if (char === '/') { + if (i === 0) { + start = i + 1 + } else { + end = i + yield Escape(pointer.slice(start, end)) + start = i + 1 + } + } else { + end = i + } + } + yield Escape(pointer.slice(start)) +} +/** Sets the value at the given pointer. If the value at the pointer does not exist it is created */ +// prettier-ignore +export function Set(value: any, pointer: string, update: unknown): void { + if (pointer === '') throw new ValuePointerRootSetError(value, pointer, update) + let [owner, next, key] = [null as any, value, ''] + for (const component of Format(pointer)) { + if (next[component] === undefined) next[component] = {} + owner = next + next = next[component] + key = component + } + owner[key] = update +} +/** Deletes a value at the given pointer */ +// prettier-ignore +export function Delete(value: any, pointer: string): void { + if (pointer === '') throw new ValuePointerRootDeleteError(value, pointer) + let [owner, next, key] = [null as any, value as any, ''] + for (const component of Format(pointer)) { + if (next[component] === undefined || next[component] === null) return + owner = next + next = next[component] + key = component + } + if (Array.isArray(owner)) { + const index = parseInt(key) + owner.splice(index, 1) + } else { + delete owner[key] + } +} +/** Returns true if a value exists at the given pointer */ +// prettier-ignore +export function Has(value: any, pointer: string): boolean { + if (pointer === '') return true + let [owner, next, key] = [null as any, value as any, ''] + for (const component of Format(pointer)) { + if (next[component] === undefined) return false + owner = next + next = next[component] + key = component + } + return Object.getOwnPropertyNames(owner).includes(key) +} +/** Gets the value at the given pointer */ +// prettier-ignore +export function Get(value: any, pointer: string): any { + if (pointer === '') return value + let current = value + for (const component of Format(pointer)) { + if (current[component] === undefined) return undefined + current = current[component] + } + return current +} diff --git a/src/value/transform.ts b/src/value/transform.ts deleted file mode 100644 index bb377f9a1..000000000 --- a/src/value/transform.ts +++ /dev/null @@ -1,439 +0,0 @@ -/*-------------------------------------------------------------------------- - -@sinclair/typebox/value - -The MIT License (MIT) - -Copyright (c) 2017-2023 Haydn Paterson (sinclair) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ----------------------------------------------------------------------------*/ - -import { IsString, IsPlainObject, IsArray, IsValueType, IsUndefined } from './guard' -import { ValueError } from '../errors/errors' -import { Deref } from './deref' -import { Check } from './check' -import * as Types from '../typebox' - -// ------------------------------------------------------------------------- -// Errors -// ------------------------------------------------------------------------- -export class TransformDecodeCheckError extends Types.TypeBoxError { - constructor(public readonly schema: Types.TSchema, public readonly value: unknown, public readonly error: ValueError) { - super(`Unable to decode due to invalid value`) - } -} -export class TransformEncodeCheckError extends Types.TypeBoxError { - constructor(public readonly schema: Types.TSchema, public readonly value: unknown, public readonly error: ValueError) { - super(`Unable to encode due to invalid value`) - } -} -export class TransformDecodeError extends Types.TypeBoxError { - constructor(public readonly schema: Types.TSchema, public readonly value: unknown, error: any) { - super(`${error instanceof Error ? error.message : 'Unknown error'}`) - } -} -export class TransformEncodeError extends Types.TypeBoxError { - constructor(public readonly schema: Types.TSchema, public readonly value: unknown, error: any) { - super(`${error instanceof Error ? error.message : 'Unknown error'}`) - } -} -// ------------------------------------------------------------------ -// HasTransform -// ------------------------------------------------------------------ -/** Recursively checks a schema for transform codecs */ -export namespace HasTransform { - function TArray(schema: Types.TArray, references: Types.TSchema[]): boolean { - return Types.TypeGuard.TTransform(schema) || Visit(schema.items, references) - } - function TAsyncIterator(schema: Types.TAsyncIterator, references: Types.TSchema[]): boolean { - return Types.TypeGuard.TTransform(schema) || Visit(schema.items, references) - } - function TConstructor(schema: Types.TConstructor, references: Types.TSchema[]) { - return Types.TypeGuard.TTransform(schema) || Visit(schema.returns, references) || schema.parameters.some((schema) => Visit(schema, references)) - } - function TFunction(schema: Types.TFunction, references: Types.TSchema[]) { - return Types.TypeGuard.TTransform(schema) || Visit(schema.returns, references) || schema.parameters.some((schema) => Visit(schema, references)) - } - function TIntersect(schema: Types.TIntersect, references: Types.TSchema[]) { - return Types.TypeGuard.TTransform(schema) || Types.TypeGuard.TTransform(schema.unevaluatedProperties) || schema.allOf.some((schema) => Visit(schema, references)) - } - function TIterator(schema: Types.TIterator, references: Types.TSchema[]) { - return Types.TypeGuard.TTransform(schema) || Visit(schema.items, references) - } - function TNot(schema: Types.TNot, references: Types.TSchema[]) { - return Types.TypeGuard.TTransform(schema) || Visit(schema.not, references) - } - function TObject(schema: Types.TObject, references: Types.TSchema[]) { - // prettier-ignore - return (Types.TypeGuard.TTransform(schema) || Object.values(schema.properties).some((schema) => Visit(schema, references)) || Types.TypeGuard.TSchema(schema.additionalProperties) && Visit(schema.additionalProperties, references) - ) - } - function TPromise(schema: Types.TPromise, references: Types.TSchema[]) { - return Types.TypeGuard.TTransform(schema) || Visit(schema.item, references) - } - function TRecord(schema: Types.TRecord, references: Types.TSchema[]) { - const pattern = Object.getOwnPropertyNames(schema.patternProperties)[0] - const property = schema.patternProperties[pattern] - return Types.TypeGuard.TTransform(schema) || Visit(property, references) || (Types.TypeGuard.TSchema(schema.additionalProperties) && Types.TypeGuard.TTransform(schema.additionalProperties)) - } - function TRef(schema: Types.TRef, references: Types.TSchema[]) { - if (Types.TypeGuard.TTransform(schema)) return true - return Visit(Deref(schema, references), references) - } - function TThis(schema: Types.TThis, references: Types.TSchema[]) { - if (Types.TypeGuard.TTransform(schema)) return true - return Visit(Deref(schema, references), references) - } - function TTuple(schema: Types.TTuple, references: Types.TSchema[]) { - return Types.TypeGuard.TTransform(schema) || (!IsUndefined(schema.items) && schema.items.some((schema) => Visit(schema, references))) - } - function TUnion(schema: Types.TUnion, references: Types.TSchema[]) { - return Types.TypeGuard.TTransform(schema) || schema.anyOf.some((schema) => Visit(schema, references)) - } - function Visit(schema: Types.TSchema, references: Types.TSchema[]): boolean { - const references_ = IsString(schema.$id) ? [...references, schema] : references - const schema_ = schema as any - if (schema.$id && visited.has(schema.$id)) return false - if (schema.$id) visited.add(schema.$id) - switch (schema[Types.Kind]) { - case 'Array': - return TArray(schema_, references_) - case 'AsyncIterator': - return TAsyncIterator(schema_, references_) - case 'Constructor': - return TConstructor(schema_, references_) - case 'Function': - return TFunction(schema_, references_) - case 'Intersect': - return TIntersect(schema_, references_) - case 'Iterator': - return TIterator(schema_, references_) - case 'Not': - return TNot(schema_, references_) - case 'Object': - return TObject(schema_, references_) - case 'Promise': - return TPromise(schema_, references_) - case 'Record': - return TRecord(schema_, references_) - case 'Ref': - return TRef(schema_, references_) - case 'This': - return TThis(schema_, references_) - case 'Tuple': - return TTuple(schema_, references_) - case 'Union': - return TUnion(schema_, references_) - default: - return Types.TypeGuard.TTransform(schema) - } - } - const visited = new Set() - /** Returns true if this schema contains a transform codec */ - export function Has(schema: Types.TSchema, references: Types.TSchema[]): boolean { - visited.clear() - return Visit(schema, references) - } -} -// ------------------------------------------------------------------ -// DecodeTransform -// ------------------------------------------------------------------ -/** Decodes a value using transform decoders if available. Does not ensure correct results. */ -export namespace DecodeTransform { - function Default(schema: Types.TSchema, value: any) { - try { - return Types.TypeGuard.TTransform(schema) ? schema[Types.Transform].Decode(value) : value - } catch (error) { - throw new TransformDecodeError(schema, value, error) - } - } - // prettier-ignore - function TArray(schema: Types.TArray, references: Types.TSchema[], value: any): any { - return (IsArray(value)) - ? Default(schema, value.map((value: any) => Visit(schema.items, references, value))) - : Default(schema, value) - } - // prettier-ignore - function TIntersect(schema: Types.TIntersect, references: Types.TSchema[], value: any) { - if (!IsPlainObject(value) || IsValueType(value)) return Default(schema, value) - const knownKeys = Types.KeyResolver.ResolveKeys(schema, { includePatterns: false }) - const knownProperties = knownKeys.reduce((value, key) => { - return (key in value) - ? { ...value, [key]: Visit(Types.IndexedAccessor.Resolve(schema, [key]), references, value[key]) } - : value - }, value) - if (!Types.TypeGuard.TTransform(schema.unevaluatedProperties)) { - return Default(schema, knownProperties) - } - const unknownKeys = Object.getOwnPropertyNames(knownProperties) - const unevaluatedProperties = schema.unevaluatedProperties as Types.TSchema - const unknownProperties = unknownKeys.reduce((value, key) => { - return !knownKeys.includes(key) - ? { ...value, [key]: Default(unevaluatedProperties, value[key]) } - : value - }, knownProperties) - return Default(schema, unknownProperties) - } - function TNot(schema: Types.TNot, references: Types.TSchema[], value: any) { - return Default(schema, Visit(schema.not, references, value)) - } - // prettier-ignore - function TObject(schema: Types.TObject, references: Types.TSchema[], value: any) { - if (!IsPlainObject(value)) return Default(schema, value) - const knownKeys = Types.KeyResolver.ResolveKeys(schema, { includePatterns: false }) - const knownProperties = knownKeys.reduce((value, key) => { - return (key in value) - ? { ...value, [key]: Visit(schema.properties[key], references, value[key]) } - : value - }, value) - if (!Types.TypeGuard.TSchema(schema.additionalProperties)) { - return Default(schema, knownProperties) - } - const unknownKeys = Object.getOwnPropertyNames(knownProperties) - const additionalProperties = schema.additionalProperties as Types.TSchema - const unknownProperties = unknownKeys.reduce((value, key) => { - return !knownKeys.includes(key) - ? { ...value, [key]: Default(additionalProperties, value[key]) } - : value - }, knownProperties) - return Default(schema, unknownProperties) - } - // prettier-ignore - function TRecord(schema: Types.TRecord, references: Types.TSchema[], value: any) { - if (!IsPlainObject(value)) return Default(schema, value) - const pattern = Object.getOwnPropertyNames(schema.patternProperties)[0] - const knownKeys = new RegExp(pattern) - const knownProperties = Object.getOwnPropertyNames(value).reduce((value, key) => { - return knownKeys.test(key) - ? { ...value, [key]: Visit(schema.patternProperties[pattern], references, value[key]) } - : value - }, value) - if (!Types.TypeGuard.TSchema(schema.additionalProperties)) { - return Default(schema, knownProperties) - } - const unknownKeys = Object.getOwnPropertyNames(knownProperties) - const additionalProperties = schema.additionalProperties as Types.TSchema - const unknownProperties = unknownKeys.reduce((value, key) => { - return !knownKeys.test(key) - ? { ...value, [key]: Default(additionalProperties, value[key]) } - : value - }, knownProperties) - return Default(schema, unknownProperties) - } - function TRef(schema: Types.TRef, references: Types.TSchema[], value: any) { - const target = Deref(schema, references) - return Default(schema, Visit(target, references, value)) - } - function TThis(schema: Types.TThis, references: Types.TSchema[], value: any) { - const target = Deref(schema, references) - return Default(schema, Visit(target, references, value)) - } - // prettier-ignore - function TTuple(schema: Types.TTuple, references: Types.TSchema[], value: any) { - return (IsArray(value) && IsArray(schema.items)) - ? Default(schema, schema.items.map((schema, index) => Visit(schema, references, value[index]))) - : Default(schema, value) - } - function TUnion(schema: Types.TUnion, references: Types.TSchema[], value: any) { - const defaulted = Default(schema, value) - for (const subschema of schema.anyOf) { - if (!Check(subschema, references, defaulted)) continue - return Visit(subschema, references, defaulted) - } - return defaulted - } - function Visit(schema: Types.TSchema, references: Types.TSchema[], value: any): any { - const references_ = typeof schema.$id === 'string' ? [...references, schema] : references - const schema_ = schema as any - switch (schema[Types.Kind]) { - case 'Array': - return TArray(schema_, references_, value) - case 'Intersect': - return TIntersect(schema_, references_, value) - case 'Not': - return TNot(schema_, references_, value) - case 'Object': - return TObject(schema_, references_, value) - case 'Record': - return TRecord(schema_, references_, value) - case 'Ref': - return TRef(schema_, references_, value) - case 'Symbol': - return Default(schema_, value) - case 'This': - return TThis(schema_, references_, value) - case 'Tuple': - return TTuple(schema_, references_, value) - case 'Union': - return TUnion(schema_, references_, value) - default: - return Default(schema_, value) - } - } - export function Decode(schema: Types.TSchema, references: Types.TSchema[], value: unknown): unknown { - return Visit(schema, references, value) - } -} -// ------------------------------------------------------------------ -// DecodeTransform -// ------------------------------------------------------------------ -/** Encodes a value using transform encoders if available. Does not ensure correct results. */ -export namespace EncodeTransform { - function Default(schema: Types.TSchema, value: any) { - try { - return Types.TypeGuard.TTransform(schema) ? schema[Types.Transform].Encode(value) : value - } catch (error) { - throw new TransformEncodeError(schema, value, error) - } - } - // prettier-ignore - function TArray(schema: Types.TArray, references: Types.TSchema[], value: any): any { - const defaulted = Default(schema, value) - return IsArray(defaulted) - ? defaulted.map((value: any) => Visit(schema.items, references, value)) - : defaulted - } - // prettier-ignore - function TIntersect(schema: Types.TIntersect, references: Types.TSchema[], value: any) { - const defaulted = Default(schema, value) - if (!IsPlainObject(value) || IsValueType(value)) return defaulted - const knownKeys = Types.KeyResolver.ResolveKeys(schema, { includePatterns: false }) - const knownProperties = knownKeys.reduce((value, key) => { - return key in defaulted - ? { ...value, [key]: Visit(Types.IndexedAccessor.Resolve(schema, [key]), references, value[key]) } - : value - }, defaulted) - if (!Types.TypeGuard.TTransform(schema.unevaluatedProperties)) { - return Default(schema, knownProperties) - } - const unknownKeys = Object.getOwnPropertyNames(knownProperties) - const unevaluatedProperties = schema.unevaluatedProperties as Types.TSchema - return unknownKeys.reduce((value, key) => { - return !knownKeys.includes(key) - ? { ...value, [key]: Default(unevaluatedProperties, value[key]) } - : value - }, knownProperties) - } - function TNot(schema: Types.TNot, references: Types.TSchema[], value: any) { - return Default(schema.not, Default(schema, value)) - } - // prettier-ignore - function TObject(schema: Types.TObject, references: Types.TSchema[], value: any) { - const defaulted = Default(schema, value) - if (!IsPlainObject(value)) return defaulted - const knownKeys = Types.KeyResolver.ResolveKeys(schema, { includePatterns: false }) - const knownProperties = knownKeys.reduce((value, key) => { - return key in value - ? { ...value, [key]: Visit(schema.properties[key], references, value[key]) } - : value - }, defaulted) - if (!Types.TypeGuard.TSchema(schema.additionalProperties)) { - return knownProperties - } - const unknownKeys = Object.getOwnPropertyNames(knownProperties) - const additionalProperties = schema.additionalProperties as Types.TSchema - return unknownKeys.reduce((value, key) => { - return !knownKeys.includes(key) - ? { ...value, [key]: Default(additionalProperties, value[key]) } - : value - }, knownProperties) - } - // prettier-ignore - function TRecord(schema: Types.TRecord, references: Types.TSchema[], value: any) { - const defaulted = Default(schema, value) as Record - if (!IsPlainObject(value)) return defaulted - const pattern = Object.getOwnPropertyNames(schema.patternProperties)[0] - const knownKeys = new RegExp(pattern) - const knownProperties = Object.getOwnPropertyNames(value).reduce((value, key) => { - return knownKeys.test(key) - ? { ...value, [key]: Visit(schema.patternProperties[pattern], references, value[key]) } - : value - }, defaulted) - if (!Types.TypeGuard.TSchema(schema.additionalProperties)) { - return Default(schema, knownProperties) - } - const unknownKeys = Object.getOwnPropertyNames(knownProperties) - const additionalProperties = schema.additionalProperties as Types.TSchema - return unknownKeys.reduce((value, key) => { - return !knownKeys.test(key) - ? { ...value, [key]: Default(additionalProperties, value[key]) } - : value - }, knownProperties) - } - function TRef(schema: Types.TRef, references: Types.TSchema[], value: any) { - const target = Deref(schema, references) - const resolved = Visit(target, references, value) - return Default(schema, resolved) - } - function TThis(schema: Types.TThis, references: Types.TSchema[], value: any) { - const target = Deref(schema, references) - const resolved = Visit(target, references, value) - return Default(schema, resolved) - } - function TTuple(schema: Types.TTuple, references: Types.TSchema[], value: any) { - const value1 = Default(schema, value) - return IsArray(schema.items) ? schema.items.map((schema, index) => Visit(schema, references, value1[index])) : [] - } - function TUnion(schema: Types.TUnion, references: Types.TSchema[], value: any) { - // test value against union variants - for (const subschema of schema.anyOf) { - if (!Check(subschema, references, value)) continue - const value1 = Visit(subschema, references, value) - return Default(schema, value1) - } - // test transformed value against union variants - for (const subschema of schema.anyOf) { - const value1 = Visit(subschema, references, value) - if (!Check(schema, references, value1)) continue - return Default(schema, value1) - } - return Default(schema, value) - } - function Visit(schema: Types.TSchema, references: Types.TSchema[], value: any): any { - const references_ = typeof schema.$id === 'string' ? [...references, schema] : references - const schema_ = schema as any - switch (schema[Types.Kind]) { - case 'Array': - return TArray(schema_, references_, value) - case 'Intersect': - return TIntersect(schema_, references_, value) - case 'Not': - return TNot(schema_, references_, value) - case 'Object': - return TObject(schema_, references_, value) - case 'Record': - return TRecord(schema_, references_, value) - case 'Ref': - return TRef(schema_, references_, value) - case 'This': - return TThis(schema_, references_, value) - case 'Tuple': - return TTuple(schema_, references_, value) - case 'Union': - return TUnion(schema_, references_, value) - default: - return Default(schema_, value) - } - } - export function Encode(schema: Types.TSchema, references: Types.TSchema[], value: unknown): unknown { - return Visit(schema, references, value) - } -} diff --git a/src/value/transform/decode.ts b/src/value/transform/decode.ts new file mode 100644 index 000000000..9cfb66f6b --- /dev/null +++ b/src/value/transform/decode.ts @@ -0,0 +1,209 @@ +/*-------------------------------------------------------------------------- + +@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 { TTransform as IsTransformType, TSchema as IsSchemaType } from '../../type/guard/type' +import { Kind, TransformKind } from '../../type/symbols/index' +import { IsPlainObject, IsArray, IsValueType } from '../guard/index' +import { ValueError } from '../../errors/index' +import { KeyOfStringResolve } from '../../type/keyof/index' +import { IndexedTypeResolve } 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' + +// ------------------------------------------------------------------ +// Errors +// ------------------------------------------------------------------ +// thrown externally +export class TransformDecodeCheckError extends Error { + 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 Error { + 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 IsTransformType(schema) ? schema[TransformKind].Decode(value) : value + } catch (error) { + throw new TransformDecodeError(schema, value, error) + } +} +// prettier-ignore +function TArray(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 TIntersect(schema: TIntersect, references: TSchema[], value: any) { + if (!IsPlainObject(value) || IsValueType(value)) return Default(schema, value) + const knownKeys = KeyOfStringResolve(schema) as string[] + const knownProperties = knownKeys.reduce((value, key) => { + return (key in value) + ? { ...value, [key]: Visit(IndexedTypeResolve(schema, [key]), references, value[key]) } + : value + }, value) + if (!IsTransformType(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 TNot(schema: TNot, references: TSchema[], value: any) { + return Default(schema, Visit(schema.not, references, value)) +} +// prettier-ignore +function TObject(schema: TObject, references: TSchema[], value: any) { + if (!IsPlainObject(value)) return Default(schema, value) + const knownKeys = KeyOfStringResolve(schema) + const knownProperties = knownKeys.reduce((value, key) => { + return (key in value) + ? { ...value, [key]: Visit(schema.properties[key], references, value[key]) } + : value + }, value) + if (!IsSchemaType(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 TRecord(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 (!IsSchemaType(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 TRef(schema: TRef, references: TSchema[], value: any) { + const target = Deref(schema, references) + return Default(schema, Visit(target, references, value)) +} +// prettier-ignore +function TThis(schema: TThis, references: TSchema[], value: any) { + const target = Deref(schema, references) + return Default(schema, Visit(target, references, value)) +} +// prettier-ignore +function TTuple(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 TUnion(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 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) + } +} +/** + * `[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 Decode(schema: TSchema, references: TSchema[], value: unknown): unknown { + return Visit(schema, references, value) +} diff --git a/src/value/transform/encode.ts b/src/value/transform/encode.ts new file mode 100644 index 000000000..6f5f2d71f --- /dev/null +++ b/src/value/transform/encode.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 { TTransform as IsTransformType, TSchema as IsSchemaType } from '../../type/guard/type' +import { Kind, TransformKind } from '../../type/symbols/index' +import { IsPlainObject, IsArray, IsValueType } from '../guard/index' +import { ValueError } from '../../errors/index' +import { KeyOfStringResolve } from '../../type/keyof/index' +import { IndexedTypeResolve } 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' + +// ------------------------------------------------------------------ +// Errors +// ------------------------------------------------------------------ +export class TransformEncodeCheckError extends Error { + 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 Error { + 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 IsTransformType(schema) ? schema[TransformKind].Encode(value) : value + } catch (error) { + throw new TransformEncodeError(schema, value, error) + } +} +// prettier-ignore +function TArray(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 TIntersect(schema: TIntersect, references: TSchema[], value: any) { + const defaulted = Default(schema, value) + if (!IsPlainObject(value) || IsValueType(value)) return defaulted + const knownKeys = KeyOfStringResolve(schema) as string[] + const knownProperties = knownKeys.reduce((value, key) => { + return key in defaulted + ? { ...value, [key]: Visit(IndexedTypeResolve(schema, [key]), references, value[key]) } + : value + }, defaulted) + if (!IsTransformType(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 TNot(schema: TNot, references: TSchema[], value: any) { + return Default(schema.not, Default(schema, value)) +} +// prettier-ignore +function TObject(schema: TObject, references: TSchema[], value: any) { + const defaulted = Default(schema, value) + if (!IsPlainObject(value)) return defaulted + const knownKeys = KeyOfStringResolve(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 (!IsSchemaType(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 TRecord(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 (!IsSchemaType(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 TRef(schema: TRef, references: TSchema[], value: any) { + const target = Deref(schema, references) + const resolved = Visit(target, references, value) + return Default(schema, resolved) +} +// prettier-ignore +function TThis(schema: TThis, references: TSchema[], value: any) { + const target = Deref(schema, references) + const resolved = Visit(target, references, value) + return Default(schema, resolved) +} +// prettier-ignore +function TTuple(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 TUnion(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 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) + } +} +/** + * `[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 Encode(schema: TSchema, references: TSchema[], value: unknown): unknown { + return Visit(schema, references, value) +} diff --git a/src/value/transform/has.ts b/src/value/transform/has.ts new file mode 100644 index 000000000..03ea4097a --- /dev/null +++ b/src/value/transform/has.ts @@ -0,0 +1,160 @@ +/*-------------------------------------------------------------------------- + +@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 { TTransform as IsTransformType, TSchema as IsSchemaType } from '../../type/guard/type' +import { IsString, IsUndefined } from '../guard/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 { 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' + +// prettier-ignore +function TArray(schema: TArray, references: TSchema[]): boolean { + return IsTransformType(schema) || Visit(schema.items, references) +} +// prettier-ignore +function TAsyncIterator(schema: TAsyncIterator, references: TSchema[]): boolean { + return IsTransformType(schema) || Visit(schema.items, references) +} +// prettier-ignore +function TConstructor(schema: TConstructor, references: TSchema[]) { + return IsTransformType(schema) || Visit(schema.returns, references) || schema.parameters.some((schema) => Visit(schema, references)) +} +// prettier-ignore +function TFunction(schema: TFunction, references: TSchema[]) { + return IsTransformType(schema) || Visit(schema.returns, references) || schema.parameters.some((schema) => Visit(schema, references)) +} +// prettier-ignore +function TIntersect(schema: TIntersect, references: TSchema[]) { + return IsTransformType(schema) || IsTransformType(schema.unevaluatedProperties) || schema.allOf.some((schema) => Visit(schema, references)) +} +// prettier-ignore +function TIterator(schema: TIterator, references: TSchema[]) { + return IsTransformType(schema) || Visit(schema.items, references) +} +// prettier-ignore +function TNot(schema: TNot, references: TSchema[]) { + return IsTransformType(schema) || Visit(schema.not, references) +} +// prettier-ignore +function TObject(schema: TObject, references: TSchema[]) { + return ( + IsTransformType(schema) || + Object.values(schema.properties).some((schema) => Visit(schema, references)) || + ( + IsSchemaType(schema.additionalProperties) && Visit(schema.additionalProperties, references) + ) + ) +} +// prettier-ignore +function TPromise(schema: TPromise, references: TSchema[]) { + return IsTransformType(schema) || Visit(schema.item, references) +} +// prettier-ignore +function TRecord(schema: TRecord, references: TSchema[]) { + const pattern = Object.getOwnPropertyNames(schema.patternProperties)[0] + const property = schema.patternProperties[pattern] + return IsTransformType(schema) || Visit(property, references) || (IsSchemaType(schema.additionalProperties) && IsTransformType(schema.additionalProperties)) +} +// prettier-ignore +function TRef(schema: TRef, references: TSchema[]) { + if (IsTransformType(schema)) return true + return Visit(Deref(schema, references), references) +} +// prettier-ignore +function TThis(schema: TThis, references: TSchema[]) { + if (IsTransformType(schema)) return true + return Visit(Deref(schema, references), references) +} +// prettier-ignore +function TTuple(schema: TTuple, references: TSchema[]) { + return IsTransformType(schema) || (!IsUndefined(schema.items) && schema.items.some((schema) => Visit(schema, references))) +} +// prettier-ignore +function TUnion(schema: TUnion, references: TSchema[]) { + return IsTransformType(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 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 IsTransformType(schema) + } +} +const visited = new Set() +/** Returns true if this schema contains a transform codec */ +export function HasTransform(schema: TSchema, references: TSchema[]): boolean { + visited.clear() + return Visit(schema, references) +} diff --git a/src/value/transform/index.ts b/src/value/transform/index.ts new file mode 100644 index 000000000..f821f6d29 --- /dev/null +++ b/src/value/transform/index.ts @@ -0,0 +1,31 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * from './decode' +export * from './encode' +export * from './has' diff --git a/src/value/value.ts b/src/value/value.ts deleted file mode 100644 index 9cf27168b..000000000 --- a/src/value/value.ts +++ /dev/null @@ -1,129 +0,0 @@ -/*-------------------------------------------------------------------------- - -@sinclair/typebox/value - -The MIT License (MIT) - -Copyright (c) 2017-2023 Haydn Paterson (sinclair) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - ----------------------------------------------------------------------------*/ - -import * as ValueErrors from '../errors/index' -import * as ValueMutate from './mutate' -import * as ValueHash from './hash' -import * as ValueEqual from './equal' -import * as ValueCast from './cast' -import * as ValueClone from './clone' -import * as ValueConvert from './convert' -import * as ValueCreate from './create' -import * as ValueCheck from './check' -import * as ValueDelta from './delta' -import * as ValueTransform from './transform' -import * as Types from '../typebox' - -/** Functions to perform structural operations on JavaScript values */ -export namespace Value { - /** Casts a value into a given type. The return value will retain as much information of the original value as possible. */ - export function Cast(schema: T, references: Types.TSchema[], value: unknown): Types.Static - /** Casts a value into a given type. The return value will retain as much information of the original value as possible. */ - export function Cast(schema: T, value: unknown): Types.Static - /** Casts a value into a given type. The return value will retain as much information of the original value as possible. */ - export function Cast(...args: any[]) { - return ValueCast.Cast.apply(ValueCast, args as any) - } - /** Creates a value from the given type and references */ - export function Create(schema: T, references: Types.TSchema[]): Types.Static - /** Creates a value from the given type */ - export function Create(schema: T): Types.Static - /** Creates a value from the given type */ - export function Create(...args: any[]) { - return ValueCreate.Create.apply(ValueCreate, args as any) - } - /** Returns true if the value matches the given type and references */ - export function Check(schema: T, references: Types.TSchema[], value: unknown): value is Types.Static - /** Returns true if the value matches the given type */ - export function Check(schema: T, value: unknown): value is Types.Static - /** Returns true if the value matches the given type */ - export function Check(...args: any[]) { - return ValueCheck.Check.apply(ValueCheck, args as any) - } - /** Converts any type mismatched values to their target type if a reasonable conversion is possible */ - export function Convert(schema: T, references: Types.TSchema[], value: unknown): unknown - /** Converts any type mismatched values to their target type if a reasonable conversion is possibl. */ - export function Convert(schema: T, value: unknown): unknown - /** Converts any type mismatched values to their target type if a reasonable conversion is possible */ - export function Convert(...args: any[]) { - return ValueConvert.Convert.apply(ValueConvert, args as any) - } - /** Returns a structural clone of the given value */ - export function Clone(value: T): T { - return ValueClone.Clone(value) - } - /** Decodes a value or throws if error */ - export function Decode>(schema: T, references: Types.TSchema[], value: unknown): R - /** Decodes a value or throws if error */ - export function Decode>(schema: T, value: unknown): R - /** Decodes a value or throws if error */ - export function Decode(...args: any[]) { - const [schema, references, value] = args.length === 3 ? [args[0], args[1], args[2]] : [args[0], [], args[1]] - if (!Check(schema, references, value)) throw new ValueTransform.TransformDecodeCheckError(schema, value, Errors(schema, references, value).First()!) - return ValueTransform.DecodeTransform.Decode(schema, references, value) - } - /** Encodes a value or throws if error */ - export function Encode>(schema: T, references: Types.TSchema[], value: unknown): R - /** Encodes a value or throws if error */ - export function Encode>(schema: T, value: unknown): R - /** Encodes a value or throws if error */ - export function Encode(...args: any[]) { - const [schema, references, value] = args.length === 3 ? [args[0], args[1], args[2]] : [args[0], [], args[1]] - const encoded = ValueTransform.EncodeTransform.Encode(schema, references, value) - if (!Check(schema, references, encoded)) throw new ValueTransform.TransformEncodeCheckError(schema, value, Errors(schema, references, value).First()!) - return encoded - } - /** Returns an iterator for each error in this value. */ - export function Errors(schema: T, references: Types.TSchema[], value: unknown): ValueErrors.ValueErrorIterator - /** Returns an iterator for each error in this value. */ - export function Errors(schema: T, value: unknown): ValueErrors.ValueErrorIterator - /** Returns an iterator for each error in this value. */ - export function Errors(...args: any[]) { - return ValueErrors.Errors.apply(ValueErrors, args as any) - } - /** Returns true if left and right values are structurally equal */ - export function Equal(left: T, right: unknown): right is T { - return ValueEqual.Equal(left, right) - } - /** Returns edits to transform the current value into the next value */ - export function Diff(current: unknown, next: unknown): ValueDelta.Edit[] { - return ValueDelta.Diff(current, next) - } - /** Returns a FNV1A-64 non cryptographic hash of the given value */ - export function Hash(value: unknown): bigint { - return ValueHash.Hash(value) - } - /** Returns a new value with edits applied to the given value */ - export function Patch(current: unknown, edits: ValueDelta.Edit[]): T { - return ValueDelta.Patch(current, edits) as T - } - /** Performs a deep mutable value assignment while retaining internal references. */ - export function Mutate(current: ValueMutate.Mutable, next: ValueMutate.Mutable): void { - ValueMutate.Mutate(current, next) - } -} diff --git a/src/value/value/index.ts b/src/value/value/index.ts new file mode 100644 index 000000000..3af158d90 --- /dev/null +++ b/src/value/value/index.ts @@ -0,0 +1,29 @@ +/*-------------------------------------------------------------------------- + +@sinclair/typebox/value + +The MIT License (MIT) + +Copyright (c) 2017-2023 Haydn Paterson (sinclair) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +---------------------------------------------------------------------------*/ + +export * as Value from './value' diff --git a/src/value/value/value.ts b/src/value/value/value.ts new file mode 100644 index 000000000..13bbce657 --- /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 { Decode as DecodeValue, Encode as EncodeValue, 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 DecodeValue(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 = EncodeValue(schema, references, value) + if (!Check(schema, references, encoded)) throw new TransformEncodeCheckError(schema, value, Errors(schema, references, value).First()!) + return encoded +} +/** Returns an iterator for each error in this value. */ +export function Errors(schema: T, references: TSchema[], value: unknown): ValueErrorIterator +/** Returns an iterator for each error in this value. */ +export function Errors(schema: T, value: unknown): ValueErrorIterator +/** Returns an iterator for each error in this value. */ +export function Errors(...args: any[]) { + return ValueErrors.apply(ValueErrors, args as any) +} +/** Returns true if left and right values are structurally equal */ +export function Equal(left: T, right: unknown): right is T { + return EqualValue(left, right) +} +/** Returns edits to transform the current value into the next value */ +export function Diff(current: unknown, next: unknown): Edit[] { + return DiffValue(current, next) +} +/** Returns a FNV1A-64 non cryptographic hash of the given value */ +export function Hash(value: unknown): bigint { + return HashValue(value) +} +/** Returns a new value with edits applied to the given value */ +export function Patch(current: unknown, edits: Edit[]): T { + return PatchValue(current, edits) as T +} +/** `[Mutable]` Performs a deep mutable value assignment while retaining internal references. */ +export function Mutate(current: Mutable, next: Mutable): void { + MutateValue(current, next) +} diff --git a/benchmark/compression/index.ts b/task/benchmark/compression/index.ts similarity index 71% rename from benchmark/compression/index.ts rename to task/benchmark/compression/index.ts index 521031315..546145c70 100644 --- a/benchmark/compression/index.ts +++ b/task/benchmark/compression/index.ts @@ -3,9 +3,9 @@ import { statSync, readdirSync } from 'fs' import { basename, extname } from 'path' export async function measure(test: string) { - await shell(`hammer build benchmark/compression/module/${test}.ts --dist target/benchmark/compression`) + await shell(`hammer build task/benchmark/compression/module/${test}.ts --dist target/benchmark/compression`) const compiled = statSync(`target/benchmark/compression/${test}.js`) - await shell(`hammer build benchmark/compression/module/${test}.ts --dist target/benchmark/compression --minify`) + await shell(`hammer build task/benchmark/compression/module/${test}.ts --dist target/benchmark/compression --minify`) const minified = statSync(`target/benchmark/compression/${test}.js`) return { test: test.padEnd(20), @@ -16,7 +16,7 @@ export async function measure(test: string) { } export async function compression() { - const tests = readdirSync('benchmark/compression/module').map((name) => basename(name, extname(name))) + const tests = readdirSync('task/benchmark/compression/module').map((name) => basename(name, extname(name))) const results = await Promise.all(tests.map((test) => measure(test))) const present = results.reduce((acc, c) => { return { ...acc, [c.test.replace(/-/g, '/')]: { Compiled: c.compiled, Minified: c.minified, Compression: `${c.ratio.toFixed(2)} x` } } diff --git a/benchmark/compression/module/typebox-compiler.ts b/task/benchmark/compression/module/typebox-compiler.ts similarity index 100% rename from benchmark/compression/module/typebox-compiler.ts rename to task/benchmark/compression/module/typebox-compiler.ts diff --git a/benchmark/compression/module/typebox-errors.ts b/task/benchmark/compression/module/typebox-errors.ts similarity index 100% rename from benchmark/compression/module/typebox-errors.ts rename to task/benchmark/compression/module/typebox-errors.ts diff --git a/benchmark/compression/module/typebox-system.ts b/task/benchmark/compression/module/typebox-system.ts similarity index 100% rename from benchmark/compression/module/typebox-system.ts rename to task/benchmark/compression/module/typebox-system.ts diff --git a/benchmark/compression/module/typebox.ts b/task/benchmark/compression/module/typebox-type.ts similarity index 100% rename from benchmark/compression/module/typebox.ts rename to task/benchmark/compression/module/typebox-type.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/task/benchmark/compression/module/typebox.ts b/task/benchmark/compression/module/typebox.ts new file mode 100644 index 000000000..abd5374e1 --- /dev/null +++ b/task/benchmark/compression/module/typebox.ts @@ -0,0 +1,3 @@ +import Type from '@sinclair/typebox' + +const T = Type.String() diff --git a/benchmark/index.ts b/task/benchmark/index.ts similarity index 100% rename from benchmark/index.ts rename to task/benchmark/index.ts diff --git a/task/benchmark/measurement/index.ts b/task/benchmark/measurement/index.ts new file mode 100644 index 000000000..5976be49d --- /dev/null +++ b/task/benchmark/measurement/index.ts @@ -0,0 +1,5 @@ +import { shell } from '@sinclair/hammer' + +export async function measurement() { + await shell(`hammer run task/benchmark/measurement/module/index.ts --dist target/benchmark/measurement`) +} diff --git a/benchmark/measurement/module/benchmark.ts b/task/benchmark/measurement/module/benchmark.ts similarity index 100% rename from benchmark/measurement/module/benchmark.ts rename to task/benchmark/measurement/module/benchmark.ts diff --git a/benchmark/measurement/module/cases.ts b/task/benchmark/measurement/module/cases.ts similarity index 100% rename from benchmark/measurement/module/cases.ts rename to task/benchmark/measurement/module/cases.ts diff --git a/benchmark/measurement/module/check.ts b/task/benchmark/measurement/module/check.ts similarity index 100% rename from benchmark/measurement/module/check.ts rename to task/benchmark/measurement/module/check.ts diff --git a/benchmark/measurement/module/compile.ts b/task/benchmark/measurement/module/compile.ts similarity index 100% rename from benchmark/measurement/module/compile.ts rename to task/benchmark/measurement/module/compile.ts 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/esm.ts b/task/build/esm.ts new file mode 100644 index 000000000..d8dc216ac --- /dev/null +++ b/task/build/esm.ts @@ -0,0 +1,57 @@ +// ------------------------------------------------------------------ +// Node-ESM-Transform +// ------------------------------------------------------------------ +import * as Path from 'node:path' +import * as Fs from 'node:fs' + +// prettier-ignore +function shouldProcess(sourcePath: string) { + const extname = Path.extname(sourcePath) + return ['.js'].includes(extname) +} +// prettier-ignore +function rewriteSpecifiers(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'`) + } +} +// prettier-ignore +function newExtension(extname: string) { + return extname === '.js' ? '.mjs' : 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 = rewriteSpecifiers(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) + } +} +/** Transforms modules within sourceDirectory to Node ESM */ +// prettier-ignore +export function convertToEsm(sourceDirectory: string) { + readDirectory(sourceDirectory) +} diff --git a/task/build/index.ts b/task/build/index.ts new file mode 100644 index 000000000..336a4ee85 --- /dev/null +++ b/task/build/index.ts @@ -0,0 +1 @@ +export * from './esm' diff --git a/test/runtime/assert/assert.ts b/test/runtime/assert/assert.ts index 2e9a92944..315eec216 100644 --- a/test/runtime/assert/assert.ts +++ b/test/runtime/assert/assert.ts @@ -1,6 +1,10 @@ import * as assert from 'assert' export namespace Assert { + export function HasProperty(value: unknown, key: K): asserts value is Record { + if (typeof value === 'object' && value !== null && key in value) return + throw new Error(`Expected value to have property '${key as string}'`) + } export function IsTrue(value: boolean): asserts value is true { return assert.strictEqual(value, true) } @@ -46,7 +50,7 @@ export namespace Assert { if (value instanceof constructor) return throw Error(`Value is not instance of ${constructor}`) } - export function IsTypeOf(value: any, type: any) { + export function IsTypeOf(value: any, type: T) { if (typeof value === type) return throw Error(`Value is not typeof ${type}`) } diff --git a/test/runtime/compiler-ajv/const.ts b/test/runtime/compiler-ajv/const.ts new file mode 100644 index 000000000..21705e276 --- /dev/null +++ b/test/runtime/compiler-ajv/const.ts @@ -0,0 +1,39 @@ +import { Type } from '@sinclair/typebox' +import { Ok } from './validate' + +describe('compiler-ajv/Const', () => { + it('Should validate 1', () => { + const T = Type.Const(1) + Ok(T, 1) + }) + it('Should validate 2', () => { + const T = Type.Const('hello') + Ok(T, 'hello') + }) + it('Should validate 3', () => { + const T = Type.Const(true) + Ok(T, true) + }) + it('Should validate 4', () => { + const T = Type.Const({ x: 1, y: 2 }) + Ok(T, { x: 1, y: 2 }) + }) + it('Should validate 5', () => { + const T = Type.Const([1, 2, 3]) + Ok(T, [1, 2, 3]) + }) + it('Should validate 6', () => { + const T = Type.Const([1, true, 'hello']) + Ok(T, [1, true, 'hello']) + }) + it('Should validate 7', () => { + const T = Type.Const({ + x: [1, 2, 3, 4], + y: { x: 1, y: 2, z: 3 }, + }) + Ok(T, { + x: [1, 2, 3, 4], + y: { x: 1, y: 2, z: 3 }, + }) + }) +}) diff --git a/test/runtime/compiler-ajv/index.ts b/test/runtime/compiler-ajv/index.ts index 0c6e11f6a..8b96c33ea 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' diff --git a/test/runtime/compiler-ajv/partial.ts b/test/runtime/compiler-ajv/partial.ts index 5e56d704a..5be5691a3 100644 --- a/test/runtime/compiler-ajv/partial.ts +++ b/test/runtime/compiler-ajv/partial.ts @@ -1,4 +1,4 @@ -import { Type, Readonly, Optional } from '@sinclair/typebox' +import { Type, ReadonlyKind, OptionalKind } from '@sinclair/typebox' import { Ok } from './validate' import { Assert } from '../assert' @@ -29,12 +29,12 @@ describe('compiler-ajv/Partial', () => { { additionalProperties: false }, ) const T = Type.Partial(A) - Assert.IsEqual(T.properties.x[Readonly], 'Readonly') - Assert.IsEqual(T.properties.x[Optional], 'Optional') - Assert.IsEqual(T.properties.y[Readonly], 'Readonly') - Assert.IsEqual(T.properties.y[Optional], 'Optional') - Assert.IsEqual(T.properties.z[Optional], 'Optional') - Assert.IsEqual(T.properties.w[Optional], 'Optional') + Assert.IsEqual(T.properties.x[ReadonlyKind], 'Readonly') + Assert.IsEqual(T.properties.x[OptionalKind], 'Optional') + Assert.IsEqual(T.properties.y[ReadonlyKind], 'Readonly') + Assert.IsEqual(T.properties.y[OptionalKind], 'Optional') + Assert.IsEqual(T.properties.z[OptionalKind], 'Optional') + Assert.IsEqual(T.properties.w[OptionalKind], 'Optional') }) it('Should inherit options from the source object', () => { const A = Type.Object( diff --git a/test/runtime/compiler-ajv/required.ts b/test/runtime/compiler-ajv/required.ts index 7df4b1875..73820cfb1 100644 --- a/test/runtime/compiler-ajv/required.ts +++ b/test/runtime/compiler-ajv/required.ts @@ -1,4 +1,4 @@ -import { Type, Readonly, Optional } from '@sinclair/typebox' +import { Type, ReadonlyKind, OptionalKind } from '@sinclair/typebox' import { Ok, Fail } from './validate' import { Assert } from '../assert' @@ -26,10 +26,10 @@ describe('compiler-ajv/Required', () => { w: Type.Number(), }) const T = Type.Required(A) - Assert.IsEqual(T.properties.x[Readonly], 'Readonly') - Assert.IsEqual(T.properties.y[Readonly], 'Readonly') - Assert.IsEqual(T.properties.z[Optional], undefined) - Assert.IsEqual(T.properties.w[Optional], undefined) + Assert.IsEqual(T.properties.x[ReadonlyKind], 'Readonly') + Assert.IsEqual(T.properties.y[ReadonlyKind], 'Readonly') + Assert.IsEqual(T.properties.z[OptionalKind], undefined) + Assert.IsEqual(T.properties.w[OptionalKind], undefined) }) it('Should inherit options from the source object', () => { const A = Type.Object( diff --git a/test/runtime/compiler/const.ts b/test/runtime/compiler/const.ts new file mode 100644 index 000000000..a8526757e --- /dev/null +++ b/test/runtime/compiler/const.ts @@ -0,0 +1,39 @@ +import { Type } from '@sinclair/typebox' +import { Ok } from './validate' + +describe('compiler/Const', () => { + it('Should validate 1', () => { + const T = Type.Const(1) + Ok(T, 1) + }) + it('Should validate 2', () => { + const T = Type.Const('hello') + Ok(T, 'hello') + }) + it('Should validate 3', () => { + const T = Type.Const(true) + Ok(T, true) + }) + it('Should validate 4', () => { + const T = Type.Const({ x: 1, y: 2 }) + Ok(T, { x: 1, y: 2 }) + }) + it('Should validate 5', () => { + const T = Type.Const([1, 2, 3]) + Ok(T, [1, 2, 3]) + }) + it('Should validate 6', () => { + const T = Type.Const([1, true, 'hello']) + Ok(T, [1, true, 'hello']) + }) + it('Should validate 7', () => { + const T = Type.Const({ + x: [1, 2, 3, 4], + y: { x: 1, y: 2, z: 3 }, + }) + Ok(T, { + x: [1, 2, 3, 4], + y: { x: 1, y: 2, z: 3 }, + }) + }) +}) diff --git a/test/runtime/compiler/index.ts b/test/runtime/compiler/index.ts index db87a1e15..a516114d6 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' diff --git a/test/runtime/compiler/partial.ts b/test/runtime/compiler/partial.ts index 870e6979b..74b71aa60 100644 --- a/test/runtime/compiler/partial.ts +++ b/test/runtime/compiler/partial.ts @@ -1,5 +1,5 @@ import { TypeSystem } from '@sinclair/typebox/system' -import { Type, Kind, Optional, Readonly } from '@sinclair/typebox' +import { Type, OptionalKind, ReadonlyKind } from '@sinclair/typebox' import { Ok, Fail } from './validate' import { strictEqual } from 'assert' @@ -30,12 +30,12 @@ describe('compiler/Partial', () => { { additionalProperties: false }, ) const T = Type.Partial(A) - strictEqual(T.properties.x[Readonly], 'Readonly') - strictEqual(T.properties.x[Optional], 'Optional') - strictEqual(T.properties.y[Readonly], 'Readonly') - strictEqual(T.properties.y[Optional], 'Optional') - strictEqual(T.properties.z[Optional], 'Optional') - strictEqual(T.properties.w[Optional], 'Optional') + strictEqual(T.properties.x[ReadonlyKind], 'Readonly') + strictEqual(T.properties.x[OptionalKind], 'Optional') + strictEqual(T.properties.y[ReadonlyKind], 'Readonly') + strictEqual(T.properties.y[OptionalKind], 'Optional') + strictEqual(T.properties.z[OptionalKind], 'Optional') + strictEqual(T.properties.w[OptionalKind], 'Optional') }) it('Should inherit options from the source object', () => { const A = Type.Object( diff --git a/test/runtime/compiler/required.ts b/test/runtime/compiler/required.ts index 14ca1bf5f..fb071fecc 100644 --- a/test/runtime/compiler/required.ts +++ b/test/runtime/compiler/required.ts @@ -1,4 +1,4 @@ -import { Type, Readonly, Optional } from '@sinclair/typebox' +import { Type, ReadonlyKind, OptionalKind } from '@sinclair/typebox' import { Ok, Fail } from './validate' import { strictEqual } from 'assert' @@ -26,10 +26,10 @@ describe('compiler/Required', () => { w: Type.Number(), }) const T = Type.Required(A) - strictEqual(T.properties.x[Readonly], 'Readonly') - strictEqual(T.properties.y[Readonly], 'Readonly') - strictEqual(T.properties.z[Optional], undefined) - strictEqual(T.properties.w[Optional], undefined) + strictEqual(T.properties.x[ReadonlyKind], 'Readonly') + strictEqual(T.properties.y[ReadonlyKind], 'Readonly') + strictEqual(T.properties.z[OptionalKind], undefined) + strictEqual(T.properties.w[OptionalKind], undefined) }) it('Should inherit options from the source object', () => { const A = Type.Object( diff --git a/test/runtime/type/extends/any.ts b/test/runtime/type/extends/any.ts index 3dd3e83ee..6373c9ae0 100644 --- a/test/runtime/type/extends/any.ts +++ b/test/runtime/type/extends/any.ts @@ -1,96 +1,95 @@ -import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import { Type, ExtendsCheck, ExtendsResult } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/extends/Any', () => { it('Should extend Any', () => { type T = any extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.Any(), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Any(), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Unknown', () => { type T = any extends unknown ? 1 : 2 - const R = TypeExtends.Extends(Type.Any(), Type.Unknown()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Any(), Type.Unknown()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend String', () => { type T = any extends string ? 1 : 2 - const R = TypeExtends.Extends(Type.Any(), Type.String()) - Assert.IsEqual(R, TypeExtendsResult.Union) + const R = ExtendsCheck(Type.Any(), Type.String()) + Assert.IsEqual(R, ExtendsResult.Union) }) it('Should extend Boolean', () => { type T = any extends boolean ? 1 : 2 - const R = TypeExtends.Extends(Type.Any(), Type.Boolean()) - Assert.IsEqual(R, TypeExtendsResult.Union) + const R = ExtendsCheck(Type.Any(), Type.Boolean()) + Assert.IsEqual(R, ExtendsResult.Union) }) it('Should extend Number', () => { type T = any extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Any(), Type.Number()) - Assert.IsEqual(R, TypeExtendsResult.Union) + const R = ExtendsCheck(Type.Any(), Type.Number()) + Assert.IsEqual(R, ExtendsResult.Union) }) it('Should extend Integer', () => { type T = any extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Any(), Type.Integer()) - Assert.IsEqual(R, TypeExtendsResult.Union) + const R = ExtendsCheck(Type.Any(), Type.Integer()) + Assert.IsEqual(R, ExtendsResult.Union) }) it('Should extend Array 1', () => { type T = any extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Any(), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.Union) + const R = ExtendsCheck(Type.Any(), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.Union) }) it('Should extend Array 2', () => { type T = any extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Any(), Type.Array(Type.String())) - Assert.IsEqual(R, TypeExtendsResult.Union) + const R = ExtendsCheck(Type.Any(), Type.Array(Type.String())) + Assert.IsEqual(R, ExtendsResult.Union) }) it('Should extend Tuple', () => { type T = any extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.Any(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.Union) + const R = ExtendsCheck(Type.Any(), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.Union) }) it('Should extend Object 1', () => { type T = any extends object ? 1 : 2 - const R = TypeExtends.Extends(Type.Any(), Type.Object({})) - Assert.IsEqual(R, TypeExtendsResult.Union) + const R = ExtendsCheck(Type.Any(), Type.Object({})) + Assert.IsEqual(R, ExtendsResult.Union) }) it('Should extend Object 2', () => { type T = any extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.Any(), Type.Object({})) - Assert.IsEqual(R, TypeExtendsResult.Union) + const R = ExtendsCheck(Type.Any(), Type.Object({})) + Assert.IsEqual(R, ExtendsResult.Union) }) it('Should extend Object 3', () => { type T = any extends { a: number } ? 1 : 2 - const R = TypeExtends.Extends(Type.Any(), Type.Object({ a: Type.Number() })) - Assert.IsEqual(R, TypeExtendsResult.Union) + const R = ExtendsCheck(Type.Any(), Type.Object({ a: Type.Number() })) + Assert.IsEqual(R, ExtendsResult.Union) }) it('Should extend Union 1', () => { type T = any extends number | string ? 1 : 2 - const R = TypeExtends.Extends(Type.Any(), Type.Union([Type.Number(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.Union) + const R = ExtendsCheck(Type.Any(), Type.Union([Type.Number(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.Union) }) it('Should extend Union 2', () => { type T = any extends any | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Any(), Type.Union([Type.Any(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Any(), Type.Union([Type.Any(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Null', () => { type T = any extends null ? 1 : 2 - const R = TypeExtends.Extends(Type.Any(), Type.Null()) - Assert.IsEqual(R, TypeExtendsResult.Union) + const R = ExtendsCheck(Type.Any(), Type.Null()) + Assert.IsEqual(R, ExtendsResult.Union) }) it('Should extend Undefined', () => { type T = any extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.Any(), Type.Undefined()) - Assert.IsEqual(R, TypeExtendsResult.Union) + const R = ExtendsCheck(Type.Any(), Type.Undefined()) + Assert.IsEqual(R, ExtendsResult.Union) }) it('Should extend Void', () => { type T = any extends void ? 1 : 2 - const R = TypeExtends.Extends(Type.Any(), Type.Void()) - Assert.IsEqual(R, TypeExtendsResult.Union) + const R = ExtendsCheck(Type.Any(), Type.Void()) + Assert.IsEqual(R, ExtendsResult.Union) }) it('Should extend Date', () => { type T = any extends Date ? 1 : 2 - const R = TypeExtends.Extends(Type.Any(), Type.Date()) - Assert.IsEqual(R, TypeExtendsResult.Union) + const R = ExtendsCheck(Type.Any(), Type.Date()) + Assert.IsEqual(R, ExtendsResult.Union) }) }) diff --git a/test/runtime/type/extends/array.ts b/test/runtime/type/extends/array.ts index b734bc8ab..2e468c80c 100644 --- a/test/runtime/type/extends/array.ts +++ b/test/runtime/type/extends/array.ts @@ -1,5 +1,4 @@ -import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import { Type, ExtendsCheck, ExtendsResult } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/extends/Array', () => { @@ -8,258 +7,258 @@ describe('type/extends/Array', () => { // ---------------------------------------------- it('Should extend Array Varying 1', () => { type T = Array extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Array(Type.Any()), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Array Varying 2', () => { type T = Array extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Array(Type.String()), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Array Varying 3', () => { type T = Array extends Array ? 1 : 2 // 1 - const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Array(Type.String())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Array(Type.Any()), Type.Array(Type.String())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Array Varying 4', () => { type T = Array extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.Number()), Type.Array(Type.String())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Array(Type.Number()), Type.Array(Type.String())) + Assert.IsEqual(R, ExtendsResult.False) }) // ---------------------------------------------- // Any // ---------------------------------------------- it('Should extend Any', () => { type T = Array extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Array(Type.Any()), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Unknown', () => { type T = Array extends unknown ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Unknown()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Array(Type.Any()), Type.Unknown()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend String', () => { type T = Array extends string ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.String()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Array(Type.Any()), Type.String()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Boolean', () => { type T = Array extends boolean ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Boolean()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Array(Type.Any()), Type.Boolean()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Number', () => { type T = Array extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Number()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Array(Type.Any()), Type.Number()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Integer', () => { type T = Array extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Integer()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Array(Type.Any()), Type.Integer()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array 1', () => { type T = Array extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Array(Type.Any()), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Array 2', () => { type T = Array extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Array(Type.Any()), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Array 3', () => { type T = Array extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Array(Type.Any()), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Tuple', () => { type T = Array extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Array(Type.Any()), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 1', () => { type T = Array extends object ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Object({})) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Array(Type.Any()), Type.Object({})) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Object 2', () => { type T = Array extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Object({})) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Array(Type.Any()), Type.Object({})) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Object 3', () => { type T = Array extends { a: number } ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Object({ a: Type.Number() })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Array(Type.Any()), Type.Object({ a: Type.Number() })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 4', () => { type T = Array extends { length: '1' } ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Object({ length: Type.Literal('1') })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Array(Type.Any()), Type.Object({ length: Type.Literal('1') })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 5', () => { type T = Array extends { length: number } ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Object({ length: Type.Number() })) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Array(Type.Any()), Type.Object({ length: Type.Number() })) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 1', () => { type T = Array extends number | string ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Union([Type.Number(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Array(Type.Any()), Type.Union([Type.Number(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 2', () => { type T = Array extends any | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Union([Type.Any(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Array(Type.Any()), Type.Union([Type.Any(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 3', () => { type T = Array extends any | Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Union([Type.Any(), Type.Array(Type.Any())])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Array(Type.Any()), Type.Union([Type.Any(), Type.Array(Type.Any())])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 4', () => { type T = Array extends any | Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Union([Type.Any(), Type.Array(Type.Any())])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Array(Type.String()), Type.Union([Type.Any(), Type.Array(Type.Any())])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 5', () => { type T = Array extends any | Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Union([Type.Any(), Type.Array(Type.String())])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Array(Type.Any()), Type.Union([Type.Any(), Type.Array(Type.String())])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Null', () => { type T = Array extends null ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Null()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Array(Type.Any()), Type.Null()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Undefined', () => { type T = Array extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.Any()), Type.Undefined()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Array(Type.Any()), Type.Undefined()) + Assert.IsEqual(R, ExtendsResult.False) }) // ---------------------------------------------- // Constrained // ---------------------------------------------- it('Should extend constrained Any', () => { type T = Array extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Array(Type.String()), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend constrained Unknown', () => { type T = Array extends unknown ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Unknown()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Array(Type.String()), Type.Unknown()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend constrained String', () => { type T = Array extends string ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.String()), Type.String()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Array(Type.String()), Type.String()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend constrained Boolean', () => { type T = Array extends boolean ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Boolean()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Array(Type.String()), Type.Boolean()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend constrained Number', () => { type T = Array extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Number()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Array(Type.String()), Type.Number()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend constrained Integer', () => { type T = Array extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Integer()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Array(Type.String()), Type.Integer()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend constrained Array 1', () => { type T = Array extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Array(Type.String()), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend constrained Array 2', () => { type T = Array extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Array(Type.String()), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend constrained Array 3', () => { type T = Array extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Array(Type.String()), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend constrained Tuple', () => { type T = Array extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Array(Type.String()), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend constrained Object 1', () => { type T = Array extends object ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Object({})) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Array(Type.String()), Type.Object({})) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend constrained Object 2', () => { type T = Array extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Object({})) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Array(Type.String()), Type.Object({})) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend constrained Object 3', () => { type T = Array extends { a: number } ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Object({ a: Type.Number() })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Array(Type.String()), Type.Object({ a: Type.Number() })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend constrained Object 4', () => { type T = Array extends { length: '1' } ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Object({ length: Type.Literal('1') })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Array(Type.String()), Type.Object({ length: Type.Literal('1') })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend constrained Object 5', () => { type T = Array extends { length: number } ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Object({ length: Type.Number() })) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Array(Type.String()), Type.Object({ length: Type.Number() })) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend constrained Union 1', () => { type T = Array extends number | string ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Union([Type.Null(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Array(Type.String()), Type.Union([Type.Null(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend constrained Union 2', () => { type T = Array extends any | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Union([Type.Any(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Array(Type.String()), Type.Union([Type.Any(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend constrained Union 3', () => { type T = Array extends any | Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Union([Type.Any(), Type.Array(Type.Any())])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Array(Type.String()), Type.Union([Type.Any(), Type.Array(Type.Any())])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend constrained Union 4', () => { type T = Array extends any | Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Union([Type.Any(), Type.Array(Type.Any())])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Array(Type.String()), Type.Union([Type.Any(), Type.Array(Type.Any())])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend constrained Union 5', () => { type T = Array extends any | Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Union([Type.Any(), Type.Array(Type.String())])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Array(Type.String()), Type.Union([Type.Any(), Type.Array(Type.String())])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend constrained Null', () => { type T = Array extends null ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Null()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Array(Type.String()), Type.Null()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend constrained Undefined', () => { type T = Array extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Undefined()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Array(Type.String()), Type.Undefined()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend constrained Void', () => { type T = Array extends void ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Void()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Array(Type.String()), Type.Void()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Date', () => { type T = Array extends Date ? 1 : 2 - const R = TypeExtends.Extends(Type.Array(Type.String()), Type.Date()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Array(Type.String()), Type.Date()) + Assert.IsEqual(R, ExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/async-iterator.ts b/test/runtime/type/extends/async-iterator.ts index 4eba4c020..9cc5392a8 100644 --- a/test/runtime/type/extends/async-iterator.ts +++ b/test/runtime/type/extends/async-iterator.ts @@ -1,5 +1,4 @@ -import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import { Type, ExtendsCheck, ExtendsResult } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/extends/AsyncIterator', () => { @@ -8,65 +7,65 @@ describe('type/extends/AsyncIterator', () => { // ---------------------------------------------- it('Should extend AsyncIterator 1', () => { type T = AsyncIterableIterator extends AsyncIterableIterator ? 1 : 2 - const R = TypeExtends.Extends(Type.AsyncIterator(Type.Any()), Type.AsyncIterator(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.AsyncIterator(Type.Any()), Type.AsyncIterator(Type.Any())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend AsyncIterator 2', () => { type T = AsyncIterableIterator extends AsyncIterableIterator ? 1 : 2 - const R = TypeExtends.Extends(Type.AsyncIterator(Type.String()), Type.AsyncIterator(Type.String())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.AsyncIterator(Type.String()), Type.AsyncIterator(Type.String())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend AsyncIterator 3', () => { type T = AsyncIterableIterator<'hello'> extends AsyncIterableIterator ? 1 : 2 - const R = TypeExtends.Extends(Type.AsyncIterator(Type.Literal('hello')), Type.AsyncIterator(Type.String())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.AsyncIterator(Type.Literal('hello')), Type.AsyncIterator(Type.String())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend AsyncIterator 4', () => { type T = AsyncIterableIterator extends AsyncIterableIterator<'hello'> ? 1 : 2 - const R = TypeExtends.Extends(Type.AsyncIterator(Type.String()), Type.AsyncIterator(Type.Literal('hello'))) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.AsyncIterator(Type.String()), Type.AsyncIterator(Type.Literal('hello'))) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend AsyncIterator 5', () => { type T = AsyncIterableIterator extends AsyncIterableIterator<'hello' | number> ? 1 : 2 - const R = TypeExtends.Extends(Type.AsyncIterator(Type.String()), Type.AsyncIterator(Type.Union([Type.Literal('hello'), Type.Number()]))) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.AsyncIterator(Type.String()), Type.AsyncIterator(Type.Union([Type.Literal('hello'), Type.Number()]))) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend AsyncIterator 6', () => { type T = AsyncIterableIterator<'hello' | number> extends AsyncIterableIterator ? 1 : 2 - const R = TypeExtends.Extends(Type.AsyncIterator(Type.Union([Type.Literal('hello'), Type.Number()])), Type.AsyncIterator(Type.String())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.AsyncIterator(Type.Union([Type.Literal('hello'), Type.Number()])), Type.AsyncIterator(Type.String())) + Assert.IsEqual(R, ExtendsResult.False) }) // -------------------------------------------------------------------- // Structural // -------------------------------------------------------------------- it('Should extends Any 1', () => { type T = AsyncIterableIterator extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.AsyncIterator(Type.Number()), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.AsyncIterator(Type.Number()), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extends Any 2', () => { type T = any extends AsyncIterableIterator ? 1 : 2 - const R = TypeExtends.Extends(Type.Any(), Type.AsyncIterator(Type.Number())) - Assert.IsEqual(R, TypeExtendsResult.Union) + const R = ExtendsCheck(Type.Any(), Type.AsyncIterator(Type.Number())) + Assert.IsEqual(R, ExtendsResult.Union) }) it('Should extends Unknown 1', () => { type T = AsyncIterableIterator extends unknown ? 1 : 2 - const R = TypeExtends.Extends(Type.AsyncIterator(Type.Number()), Type.Unknown()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.AsyncIterator(Type.Number()), Type.Unknown()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extends Unknown 2', () => { type T = unknown extends AsyncIterableIterator ? 1 : 2 - const R = TypeExtends.Extends(Type.Unknown(), Type.AsyncIterator(Type.Number())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Unknown(), Type.AsyncIterator(Type.Number())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extends Never 1', () => { type T = AsyncIterableIterator extends never ? 1 : 2 - const R = TypeExtends.Extends(Type.AsyncIterator(Type.Number()), Type.Never()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.AsyncIterator(Type.Number()), Type.Never()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extends Never 2', () => { type T = never extends AsyncIterableIterator ? 1 : 2 - const R = TypeExtends.Extends(Type.Never(), Type.AsyncIterator(Type.Number())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Never(), Type.AsyncIterator(Type.Number())) + Assert.IsEqual(R, ExtendsResult.True) }) }) diff --git a/test/runtime/type/extends/bigint.ts b/test/runtime/type/extends/bigint.ts index 5346cfa39..f965eab3d 100644 --- a/test/runtime/type/extends/bigint.ts +++ b/test/runtime/type/extends/bigint.ts @@ -1,101 +1,100 @@ -import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import { Type, ExtendsCheck, ExtendsResult } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/extends/BigInt', () => { it('Should extend Any', () => { type T = bigint extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.BigInt(), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.BigInt(), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Unknown', () => { type T = bigint extends unknown ? 1 : 2 - const R = TypeExtends.Extends(Type.BigInt(), Type.Unknown()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.BigInt(), Type.Unknown()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend String', () => { type T = bigint extends string ? 1 : 2 - const R = TypeExtends.Extends(Type.BigInt(), Type.String()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.BigInt(), Type.String()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Boolean', () => { type T = bigint extends boolean ? 1 : 2 - const R = TypeExtends.Extends(Type.BigInt(), Type.Boolean()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.BigInt(), Type.Boolean()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Number', () => { type T = bigint extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.BigInt(), Type.Number()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.BigInt(), Type.Number()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Integer', () => { type T = bigint extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.BigInt(), Type.Integer()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.BigInt(), Type.Integer()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array', () => { type T = bigint extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.BigInt(), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.BigInt(), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Tuple', () => { type T = bigint extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.BigInt(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.BigInt(), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Record', () => { type T = bigint extends Record ? 1 : 2 - const R = TypeExtends.Extends(Type.BigInt(), Type.Record(Type.Number(), Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.BigInt(), Type.Record(Type.Number(), Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 1', () => { type T = bigint extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.BigInt(), Type.Object({})) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.BigInt(), Type.Object({})) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Object 2', () => { type T = bigint extends { a: 10 } ? 1 : 2 - const R = TypeExtends.Extends(Type.BigInt(), Type.Object({ a: Type.Literal(10) })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.BigInt(), Type.Object({ a: Type.Literal(10) })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 1', () => { type T = bigint extends number | string ? 1 : 2 - const R = TypeExtends.Extends(Type.BigInt(), Type.Union([Type.Number(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.BigInt(), Type.Union([Type.Number(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 2', () => { type T = bigint extends any | number ? 1 : 2 - const R = TypeExtends.Extends(Type.BigInt(), Type.Union([Type.Any(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.BigInt(), Type.Union([Type.Any(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 3', () => { type T = bigint extends boolean | number ? 1 : 2 - const R = TypeExtends.Extends(Type.BigInt(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.BigInt(), Type.Union([Type.Boolean(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 4', () => { type T = bigint extends boolean | bigint ? 1 : 2 - const R = TypeExtends.Extends(Type.BigInt(), Type.Union([Type.Boolean(), Type.BigInt()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.BigInt(), Type.Union([Type.Boolean(), Type.BigInt()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Null', () => { type T = bigint extends null ? 1 : 2 - const R = TypeExtends.Extends(Type.BigInt(), Type.Null()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.BigInt(), Type.Null()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Undefined', () => { type T = bigint extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.BigInt(), Type.Undefined()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.BigInt(), Type.Undefined()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Void', () => { type T = bigint extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.BigInt(), Type.Void()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.BigInt(), Type.Void()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Date', () => { type T = bigint extends Date ? 1 : 2 - const R = TypeExtends.Extends(Type.BigInt(), Type.Date()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.BigInt(), Type.Date()) + Assert.IsEqual(R, ExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/boolean.ts b/test/runtime/type/extends/boolean.ts index 1d56e21eb..6e336f331 100644 --- a/test/runtime/type/extends/boolean.ts +++ b/test/runtime/type/extends/boolean.ts @@ -1,91 +1,90 @@ -import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import { Type, ExtendsCheck, ExtendsResult } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/extends/Boolean', () => { it('Should extend Any', () => { type T = boolean extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.Boolean(), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Boolean(), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend String', () => { type T = boolean extends string ? 1 : 2 - const R = TypeExtends.Extends(Type.Boolean(), Type.String()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Boolean(), Type.String()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Boolean', () => { type T = boolean extends boolean ? 1 : 2 - const R = TypeExtends.Extends(Type.Boolean(), Type.Boolean()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Boolean(), Type.Boolean()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Number', () => { type T = boolean extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Boolean(), Type.Number()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Boolean(), Type.Number()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Integer', () => { type T = boolean extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Boolean(), Type.Integer()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Boolean(), Type.Integer()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array', () => { type T = boolean extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Boolean(), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Boolean(), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Tuple', () => { type T = boolean extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.Boolean(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Boolean(), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Record', () => { type T = boolean extends Record ? 1 : 2 - const R = TypeExtends.Extends(Type.Number(), Type.Record(Type.Number(), Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Number(), Type.Record(Type.Number(), Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 1', () => { type T = boolean extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.Boolean(), Type.Object({}, { additionalProperties: false })) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Boolean(), Type.Object({}, { additionalProperties: false })) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Object 2', () => { type T = boolean extends { a: 10 } ? 1 : 2 - const R = TypeExtends.Extends(Type.Boolean(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Boolean(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 1', () => { type T = boolean extends number | string ? 1 : 2 - const R = TypeExtends.Extends(Type.Boolean(), Type.Union([Type.Number(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Boolean(), Type.Union([Type.Number(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 2', () => { type T = boolean extends any | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Boolean(), Type.Union([Type.Any(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Boolean(), Type.Union([Type.Any(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 2', () => { type T = boolean extends boolean | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Boolean(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Boolean(), Type.Union([Type.Boolean(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Null', () => { type T = boolean extends null ? 1 : 2 - const R = TypeExtends.Extends(Type.Boolean(), Type.Null()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Boolean(), Type.Null()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Undefined', () => { type T = boolean extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.Boolean(), Type.Undefined()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Boolean(), Type.Undefined()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Void', () => { type T = boolean extends void ? 1 : 2 - const R = TypeExtends.Extends(Type.Boolean(), Type.Void()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Boolean(), Type.Void()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Date', () => { type T = boolean extends Date ? 1 : 2 - const R = TypeExtends.Extends(Type.Boolean(), Type.Date()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Boolean(), Type.Date()) + Assert.IsEqual(R, ExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/constructor.ts b/test/runtime/type/extends/constructor.ts index f5b6151d9..a590c842f 100644 --- a/test/runtime/type/extends/constructor.ts +++ b/test/runtime/type/extends/constructor.ts @@ -1,211 +1,210 @@ -import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import { Type, ExtendsCheck, ExtendsResult } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/extends/Constructor', () => { it('Should extend Function', () => { type T = (new () => number) extends () => number ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Function([], Type.Number())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Constructor([], Type.Number()), Type.Function([], Type.Number())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Constructor 1', () => { type T = (new () => number) extends new () => number ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Constructor([], Type.Number())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Constructor([], Type.Number()), Type.Constructor([], Type.Number())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Constructor 2', () => { type T = (new () => any) extends new () => number ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Any()), Type.Constructor([], Type.Number())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Constructor([], Type.Any()), Type.Constructor([], Type.Number())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Constructor 3', () => { type T = (new () => number) extends new () => any ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Any()), Type.Constructor([], Type.Number())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Constructor([], Type.Any()), Type.Constructor([], Type.Number())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Constructor 4', () => { type T = (new (a: number) => number) extends new () => any ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([Type.Number()], Type.Number()), Type.Constructor([], Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Constructor([Type.Number()], Type.Number()), Type.Constructor([], Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Constructor 5', () => { type T = (new (a: number | string) => number) extends new (a: number) => number ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([Type.Union([Type.Number(), Type.String()])], Type.Number()), Type.Constructor([Type.Number()], Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Constructor([Type.Union([Type.Number(), Type.String()])], Type.Number()), Type.Constructor([Type.Number()], Type.Any())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Constructor 6', () => { type T = (new (a: number) => number) extends new (a: number | string) => any ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([Type.Number()], Type.Number()), Type.Constructor([Type.Union([Type.Number(), Type.String()])], Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Constructor([Type.Number()], Type.Number()), Type.Constructor([Type.Union([Type.Number(), Type.String()])], Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Constructor 7', () => { type T = (new (a: number, b: number) => number) extends new (a: number) => number ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([Type.Number(), Type.Number()], Type.Number()), Type.Constructor([Type.Number()], Type.Number())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Constructor([Type.Number(), Type.Number()], Type.Number()), Type.Constructor([Type.Number()], Type.Number())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Constructor 8', () => { type T = (new (a: number) => number) extends new (a: number, b: number) => number ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([Type.Number()], Type.Number()), Type.Constructor([Type.Number(), Type.Number()], Type.Number())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Constructor([Type.Number()], Type.Number()), Type.Constructor([Type.Number(), Type.Number()], Type.Number())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Constructor 9', () => { type T = (new () => number) extends new () => any ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Constructor([], Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Constructor([], Type.Number()), Type.Constructor([], Type.Any())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Constructor 9', () => { type T = (new () => any) extends new () => number ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Any()), Type.Constructor([], Type.Number())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Constructor([], Type.Any()), Type.Constructor([], Type.Number())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Constructor 10', () => { type T = (new () => Array) extends new () => object ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Array(Type.Any())), Type.Constructor([], Type.Object({}))) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Constructor([], Type.Array(Type.Any())), Type.Constructor([], Type.Object({}))) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Constructor 11', () => { type T = (new () => Array) extends new () => object ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Array(Type.String())), Type.Constructor([], Type.Object({}))) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Constructor([], Type.Array(Type.String())), Type.Constructor([], Type.Object({}))) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Constructor 12', () => { type T = (new () => object) extends new () => Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Object({})), Type.Constructor([], Type.Array(Type.Any()))) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Constructor([], Type.Object({})), Type.Constructor([], Type.Array(Type.Any()))) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Constructor 13', () => { type T = (new (a: unknown) => number) extends new (a: any) => number ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([Type.Unknown()], Type.Number({})), Type.Constructor([Type.Any()], Type.Number({}))) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Constructor([Type.Unknown()], Type.Number({})), Type.Constructor([Type.Any()], Type.Number({}))) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Constructor 14', () => { type T = (new (a: any) => number) extends new (a: unknown) => number ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([Type.Any()], Type.Number({})), Type.Constructor([Type.Unknown()], Type.Number({}))) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Constructor([Type.Any()], Type.Number({})), Type.Constructor([Type.Unknown()], Type.Number({}))) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Constructor 15', () => { type T = (new () => any) extends new () => unknown ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Any({})), Type.Constructor([], Type.Unknown({}))) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Constructor([], Type.Any({})), Type.Constructor([], Type.Unknown({}))) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Constructor 16', () => { type T = (new () => unknown) extends new () => any ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Unknown({})), Type.Constructor([], Type.Any({}))) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Constructor([], Type.Unknown({})), Type.Constructor([], Type.Any({}))) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Any', () => { type T = (new () => number) extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Constructor([], Type.Number()), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend String', () => { type T = (new () => number) extends string ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.String()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Constructor([], Type.Number()), Type.String()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Boolean', () => { type T = (new () => number) extends boolean ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Boolean()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Constructor([], Type.Number()), Type.Boolean()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Number', () => { type T = (new () => number) extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Number()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Constructor([], Type.Number()), Type.Number()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Integer', () => { type T = (new () => number) extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Integer()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Constructor([], Type.Number()), Type.Integer()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array 1', () => { type T = (new () => number) extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Constructor([], Type.Number()), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array 2', () => { type T = (new () => number) extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Constructor([], Type.Number()), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array 3', () => { type T = (new () => number) extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Constructor([], Type.Number()), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Tuple', () => { type T = (new () => number) extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Constructor([], Type.Number()), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Record', () => { type T = (() => number) extends Record ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Record(Type.Number(), Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Constructor([], Type.Number()), Type.Record(Type.Number(), Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 1', () => { type T = (new () => number) extends object ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Object({})) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Constructor([], Type.Number()), Type.Object({})) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Object 2', () => { type T = (new () => number) extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Object({})) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Constructor([], Type.Number()), Type.Object({})) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Object 3', () => { type T = (new () => number) extends { a: number } ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Object({ a: Type.Number() })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Constructor([], Type.Number()), Type.Object({ a: Type.Number() })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 4', () => { type T = (new () => number) extends { length: '1' } ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Object({ length: Type.Literal('1') })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Constructor([], Type.Number()), Type.Object({ length: Type.Literal('1') })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 1', () => { type T = (new () => number) extends number | string ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Union([Type.Null(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Constructor([], Type.Number()), Type.Union([Type.Null(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 2', () => { type T = (new () => number) extends any | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Union([Type.Any(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Constructor([], Type.Number()), Type.Union([Type.Any(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 3', () => { type T = (new () => number) extends any | Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.Any())])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Constructor([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.Any())])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 4', () => { type T = (new () => number) extends any | Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.Any())])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Constructor([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.Any())])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 5', () => { type T = (new () => number) extends any | Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.String())])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Constructor([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.String())])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Null', () => { type T = (new () => number) extends null ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Null()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Constructor([], Type.Number()), Type.Null()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Undefined', () => { type T = (new () => number) extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Undefined()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Constructor([], Type.Number()), Type.Undefined()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Void', () => { type T = (new () => number) extends void ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Void()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Constructor([], Type.Number()), Type.Void()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Date', () => { type T = (new () => number) extends Date ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Date()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Constructor([], Type.Number()), Type.Date()) + Assert.IsEqual(R, ExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/date.ts b/test/runtime/type/extends/date.ts index 04df6c661..2bcfa8864 100644 --- a/test/runtime/type/extends/date.ts +++ b/test/runtime/type/extends/date.ts @@ -1,96 +1,95 @@ -import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import { Type, ExtendsCheck, ExtendsResult } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/extends/Date', () => { it('Should extend Any', () => { type T = Date extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.Date(), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Date(), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Unknown', () => { type T = Date extends unknown ? 1 : 2 - const R = TypeExtends.Extends(Type.Date(), Type.Unknown()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Date(), Type.Unknown()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend String', () => { type T = Date extends string ? 1 : 2 - const R = TypeExtends.Extends(Type.Date(), Type.String()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Date(), Type.String()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Boolean', () => { type T = Date extends boolean ? 1 : 2 - const R = TypeExtends.Extends(Type.Date(), Type.Boolean()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Date(), Type.Boolean()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Number', () => { type T = Date extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Date(), Type.Number()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Date(), Type.Number()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Integer', () => { type T = Date extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Date(), Type.Integer()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Date(), Type.Integer()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array', () => { type T = Date extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Date(), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Date(), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Tuple', () => { type T = Date extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.Date(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Date(), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Record', () => { type T = Date extends Record ? 1 : 2 - const R = TypeExtends.Extends(Type.Date(), Type.Record(Type.Number(), Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Date(), Type.Record(Type.Number(), Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 1', () => { type T = Date extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.Date(), Type.Object({})) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Date(), Type.Object({})) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Object 2', () => { type T = Date extends { a: 10 } ? 1 : 2 - const R = TypeExtends.Extends(Type.Date(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Date(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 1', () => { type T = Date extends number | string ? 1 : 2 - const R = TypeExtends.Extends(Type.Date(), Type.Union([Type.Number(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Date(), Type.Union([Type.Number(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 2', () => { type T = Date extends any | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Date(), Type.Union([Type.Any(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Date(), Type.Union([Type.Any(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 3', () => { type T = Date extends boolean | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Date(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Date(), Type.Union([Type.Boolean(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Null', () => { type T = Date extends null ? 1 : 2 - const R = TypeExtends.Extends(Type.Date(), Type.Null()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Date(), Type.Null()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Undefined', () => { type T = Date extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.Date(), Type.Undefined()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Date(), Type.Undefined()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Void', () => { type T = Date extends void ? 1 : 2 - const R = TypeExtends.Extends(Type.Date(), Type.Void()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Date(), Type.Void()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Date', () => { type T = Date extends Date ? 1 : 2 - const R = TypeExtends.Extends(Type.Date(), Type.Date()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Date(), Type.Date()) + Assert.IsEqual(R, ExtendsResult.True) }) }) diff --git a/test/runtime/type/extends/function.ts b/test/runtime/type/extends/function.ts index 0b334087d..e889555a3 100644 --- a/test/runtime/type/extends/function.ts +++ b/test/runtime/type/extends/function.ts @@ -1,211 +1,210 @@ -import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import { Type, ExtendsCheck, ExtendsResult } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/extends/Function', () => { it('Should extend Constructor 1', () => { type T = (() => number) extends new () => number ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Constructor([], Type.Number())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Function([], Type.Number()), Type.Constructor([], Type.Number())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Function 1', () => { type T = (() => number) extends () => number ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Function([], Type.Number())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Function([], Type.Number()), Type.Function([], Type.Number())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Function 2', () => { type T = (() => any) extends () => number ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Any()), Type.Function([], Type.Number())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Function([], Type.Any()), Type.Function([], Type.Number())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Function 3', () => { type T = (() => number) extends () => any ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Any()), Type.Function([], Type.Number())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Function([], Type.Any()), Type.Function([], Type.Number())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Function 4', () => { type T = ((a: number) => number) extends () => any ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([Type.Number()], Type.Number()), Type.Function([], Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Function([Type.Number()], Type.Number()), Type.Function([], Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Function 5', () => { type T = ((a: number | string) => number) extends (a: number) => number ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([Type.Union([Type.Number(), Type.String()])], Type.Number()), Type.Function([Type.Number()], Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Function([Type.Union([Type.Number(), Type.String()])], Type.Number()), Type.Function([Type.Number()], Type.Any())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Function 6', () => { type T = ((a: number) => number) extends (a: number | string) => any ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([Type.Number()], Type.Number()), Type.Function([Type.Union([Type.Number(), Type.String()])], Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Function([Type.Number()], Type.Number()), Type.Function([Type.Union([Type.Number(), Type.String()])], Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Function 7', () => { type T = ((a: number, b: number) => number) extends (a: number) => number ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([Type.Number(), Type.Number()], Type.Number()), Type.Function([Type.Number()], Type.Number())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Function([Type.Number(), Type.Number()], Type.Number()), Type.Function([Type.Number()], Type.Number())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Function 8', () => { type T = ((a: number) => number) extends (a: number, b: number) => number ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([Type.Number()], Type.Number()), Type.Function([Type.Number(), Type.Number()], Type.Number())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Function([Type.Number()], Type.Number()), Type.Function([Type.Number(), Type.Number()], Type.Number())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Function 9', () => { type T = (() => number) extends () => any ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Function([], Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Function([], Type.Number()), Type.Function([], Type.Any())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Function 9', () => { type T = (() => any) extends () => number ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Any()), Type.Function([], Type.Number())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Function([], Type.Any()), Type.Function([], Type.Number())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Function 10', () => { type T = (() => Array) extends () => object ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Array(Type.Any())), Type.Function([], Type.Object({}))) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Function([], Type.Array(Type.Any())), Type.Function([], Type.Object({}))) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Function 11', () => { type T = (() => Array) extends () => object ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Array(Type.String())), Type.Function([], Type.Object({}))) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Function([], Type.Array(Type.String())), Type.Function([], Type.Object({}))) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Function 12', () => { type T = (() => object) extends () => Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Object({})), Type.Function([], Type.Array(Type.Any()))) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Function([], Type.Object({})), Type.Function([], Type.Array(Type.Any()))) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Function 13', () => { type T = ((a: unknown) => number) extends (a: any) => number ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([Type.Unknown()], Type.Number({})), Type.Function([Type.Any()], Type.Number({}))) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Function([Type.Unknown()], Type.Number({})), Type.Function([Type.Any()], Type.Number({}))) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Function 14', () => { type T = ((a: any) => number) extends (a: unknown) => number ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([Type.Any()], Type.Number({})), Type.Function([Type.Unknown()], Type.Number({}))) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Function([Type.Any()], Type.Number({})), Type.Function([Type.Unknown()], Type.Number({}))) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Function 15', () => { type T = (() => any) extends () => unknown ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Any({})), Type.Function([], Type.Unknown({}))) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Function([], Type.Any({})), Type.Function([], Type.Unknown({}))) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Function 16', () => { type T = (() => unknown) extends () => any ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Unknown({})), Type.Function([], Type.Any({}))) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Function([], Type.Unknown({})), Type.Function([], Type.Any({}))) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Any', () => { type T = (() => number) extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Function([], Type.Number()), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend String', () => { type T = (() => number) extends string ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.String()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Function([], Type.Number()), Type.String()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Boolean', () => { type T = (() => number) extends boolean ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Boolean()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Function([], Type.Number()), Type.Boolean()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Number', () => { type T = (() => number) extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Number()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Function([], Type.Number()), Type.Number()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Integer', () => { type T = (() => number) extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Integer()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Function([], Type.Number()), Type.Integer()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array 1', () => { type T = (() => number) extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Function([], Type.Number()), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array 2', () => { type T = (() => number) extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Function([], Type.Number()), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array 3', () => { type T = (() => number) extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Function([], Type.Number()), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Tuple', () => { type T = (() => number) extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Function([], Type.Number()), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Record', () => { type T = (() => number) extends Record ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Record(Type.Number(), Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Function([], Type.Number()), Type.Record(Type.Number(), Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 1', () => { type T = (() => number) extends object ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Object({})) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Function([], Type.Number()), Type.Object({})) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Object 2', () => { type T = (() => number) extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Object({})) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Function([], Type.Number()), Type.Object({})) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Object 3', () => { type T = (() => number) extends { a: number } ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Object({ a: Type.Number() })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Function([], Type.Number()), Type.Object({ a: Type.Number() })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 4', () => { type T = (() => number) extends { length: '1' } ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Object({ length: Type.Literal('1') })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Function([], Type.Number()), Type.Object({ length: Type.Literal('1') })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 5', () => { type T = (() => number) extends { length: number } ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Object({ length: Type.Number() })) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Function([], Type.Number()), Type.Object({ length: Type.Number() })) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 1', () => { type T = (() => number) extends number | string ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Union([Type.Null(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Function([], Type.Number()), Type.Union([Type.Null(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 2', () => { type T = (() => number) extends any | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Union([Type.Any(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Function([], Type.Number()), Type.Union([Type.Any(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 3', () => { type T = (() => number) extends any | Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.Any())])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Function([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.Any())])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 4', () => { type T = (() => number) extends any | Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.Any())])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Function([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.Any())])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 5', () => { type T = (() => number) extends any | Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.String())])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Function([], Type.Number()), Type.Union([Type.Any(), Type.Array(Type.String())])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Null', () => { type T = (() => number) extends null ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Null()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Function([], Type.Number()), Type.Null()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Void', () => { type T = (() => number) extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.Function([], Type.Number()), Type.Void()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Function([], Type.Number()), Type.Void()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Date', () => { type T = (() => number) extends Date ? 1 : 2 - const R = TypeExtends.Extends(Type.Constructor([], Type.Number()), Type.Date()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Constructor([], Type.Number()), Type.Date()) + Assert.IsEqual(R, ExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/integer.ts b/test/runtime/type/extends/integer.ts index 1c5d00995..6abd57529 100644 --- a/test/runtime/type/extends/integer.ts +++ b/test/runtime/type/extends/integer.ts @@ -1,96 +1,95 @@ -import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import { Type, ExtendsCheck, ExtendsResult } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/extends/Integer', () => { it('Should extend Any', () => { type T = number extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.Integer(), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Integer(), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Unknown', () => { type T = number extends unknown ? 1 : 2 - const R = TypeExtends.Extends(Type.Integer(), Type.Unknown()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Integer(), Type.Unknown()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend String', () => { type T = number extends string ? 1 : 2 - const R = TypeExtends.Extends(Type.Integer(), Type.String()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Integer(), Type.String()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Boolean', () => { type T = number extends boolean ? 1 : 2 - const R = TypeExtends.Extends(Type.Integer(), Type.Boolean()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Integer(), Type.Boolean()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Number', () => { type T = number extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Integer(), Type.Number()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Integer(), Type.Number()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Integer', () => { type T = number extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Integer(), Type.Integer()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Integer(), Type.Integer()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Array', () => { type T = number extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Integer(), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Integer(), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Tuple', () => { type T = number extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.Integer(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Integer(), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Record', () => { type T = number extends Record ? 1 : 2 - const R = TypeExtends.Extends(Type.Integer(), Type.Record(Type.Number(), Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Integer(), Type.Record(Type.Number(), Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 1', () => { type T = number extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.Integer(), Type.Object({})) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Integer(), Type.Object({})) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Object 2', () => { type T = number extends { a: 10 } ? 1 : 2 - const R = TypeExtends.Extends(Type.Integer(), Type.Object({ a: Type.Literal(10) })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Integer(), Type.Object({ a: Type.Literal(10) })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 1', () => { type T = number extends number | string ? 1 : 2 - const R = TypeExtends.Extends(Type.Integer(), Type.Union([Type.Number(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Integer(), Type.Union([Type.Number(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 2', () => { type T = number extends any | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Integer(), Type.Union([Type.Any(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Integer(), Type.Union([Type.Any(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 2', () => { type T = number extends boolean | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Integer(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Integer(), Type.Union([Type.Boolean(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Null', () => { type T = number extends null ? 1 : 2 - const R = TypeExtends.Extends(Type.Integer(), Type.Null()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Integer(), Type.Null()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Undefined', () => { type T = number extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.Integer(), Type.Undefined()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Integer(), Type.Undefined()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Void', () => { type T = number extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.Integer(), Type.Void()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Integer(), Type.Void()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Date', () => { type T = number extends Date ? 1 : 2 - const R = TypeExtends.Extends(Type.Number(), Type.Date()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Number(), Type.Date()) + Assert.IsEqual(R, ExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/iterator.ts b/test/runtime/type/extends/iterator.ts index 1cf5b6bfd..d685dfab1 100644 --- a/test/runtime/type/extends/iterator.ts +++ b/test/runtime/type/extends/iterator.ts @@ -1,5 +1,4 @@ -import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import { Type, ExtendsCheck, ExtendsResult } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/extends/Iterator', () => { @@ -8,65 +7,65 @@ describe('type/extends/Iterator', () => { // ---------------------------------------------- it('Should extend Iterator 1', () => { type T = IterableIterator extends IterableIterator ? 1 : 2 - const R = TypeExtends.Extends(Type.Iterator(Type.Any()), Type.Iterator(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Iterator(Type.Any()), Type.Iterator(Type.Any())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Iterator 2', () => { type T = IterableIterator extends IterableIterator ? 1 : 2 - const R = TypeExtends.Extends(Type.Iterator(Type.String()), Type.Iterator(Type.String())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Iterator(Type.String()), Type.Iterator(Type.String())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Iterator 3', () => { type T = IterableIterator<'hello'> extends IterableIterator ? 1 : 2 - const R = TypeExtends.Extends(Type.Iterator(Type.Literal('hello')), Type.Iterator(Type.String())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Iterator(Type.Literal('hello')), Type.Iterator(Type.String())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Iterator 4', () => { type T = IterableIterator extends IterableIterator<'hello'> ? 1 : 2 - const R = TypeExtends.Extends(Type.Iterator(Type.String()), Type.Iterator(Type.Literal('hello'))) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Iterator(Type.String()), Type.Iterator(Type.Literal('hello'))) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Iterator 5', () => { type T = IterableIterator extends IterableIterator<'hello' | number> ? 1 : 2 - const R = TypeExtends.Extends(Type.Iterator(Type.String()), Type.Iterator(Type.Union([Type.Literal('hello'), Type.Number()]))) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Iterator(Type.String()), Type.Iterator(Type.Union([Type.Literal('hello'), Type.Number()]))) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Iterator 6', () => { type T = IterableIterator<'hello' | number> extends IterableIterator ? 1 : 2 - const R = TypeExtends.Extends(Type.Iterator(Type.Union([Type.Literal('hello'), Type.Number()])), Type.Iterator(Type.String())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Iterator(Type.Union([Type.Literal('hello'), Type.Number()])), Type.Iterator(Type.String())) + Assert.IsEqual(R, ExtendsResult.False) }) // -------------------------------------------------------------------- // Structural // -------------------------------------------------------------------- it('Should extends Any 1', () => { type T = IterableIterator extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.Iterator(Type.Number()), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Iterator(Type.Number()), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extends Any 2', () => { type T = any extends IterableIterator ? 1 : 2 - const R = TypeExtends.Extends(Type.Any(), Type.Iterator(Type.Number())) - Assert.IsEqual(R, TypeExtendsResult.Union) + const R = ExtendsCheck(Type.Any(), Type.Iterator(Type.Number())) + Assert.IsEqual(R, ExtendsResult.Union) }) it('Should extends Unknown 1', () => { type T = IterableIterator extends unknown ? 1 : 2 - const R = TypeExtends.Extends(Type.Iterator(Type.Number()), Type.Unknown()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Iterator(Type.Number()), Type.Unknown()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extends Unknown 2', () => { type T = unknown extends IterableIterator ? 1 : 2 - const R = TypeExtends.Extends(Type.Unknown(), Type.Iterator(Type.Number())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Unknown(), Type.Iterator(Type.Number())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extends Never 1', () => { type T = IterableIterator extends never ? 1 : 2 - const R = TypeExtends.Extends(Type.Iterator(Type.Number()), Type.Never()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Iterator(Type.Number()), Type.Never()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extends Never 2', () => { type T = never extends IterableIterator ? 1 : 2 - const R = TypeExtends.Extends(Type.Never(), Type.Iterator(Type.Number())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Never(), Type.Iterator(Type.Number())) + Assert.IsEqual(R, ExtendsResult.True) }) }) diff --git a/test/runtime/type/extends/literal.ts b/test/runtime/type/extends/literal.ts index 7c23890fc..ca2c952b6 100644 --- a/test/runtime/type/extends/literal.ts +++ b/test/runtime/type/extends/literal.ts @@ -1,5 +1,4 @@ -import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import { Type, ExtendsCheck, ExtendsResult } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/extends/Literal', () => { @@ -8,258 +7,258 @@ describe('type/extends/Literal', () => { // ------------------------------------------------------------------- it('Should extend Any (String)', () => { type T = 'hello' extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal('hello'), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Literal('hello'), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Unknown (String)', () => { type T = 'hello' extends unknown ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal('hello'), Type.Unknown()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Literal('hello'), Type.Unknown()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend String (String)', () => { type T = 'hello' extends string ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal('hello'), Type.String()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Literal('hello'), Type.String()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Boolean (String)', () => { type T = 'hello' extends boolean ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal('hello'), Type.Boolean()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Literal('hello'), Type.Boolean()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Number (String)', () => { type T = 'hello' extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal('hello'), Type.Number()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Literal('hello'), Type.Number()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Integer (String)', () => { type T = 'hello' extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal('hello'), Type.Integer()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Literal('hello'), Type.Integer()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array (String)', () => { type T = 'hello' extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal('hello'), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Literal('hello'), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Tuple (String)', () => { type T = 'hello' extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal('hello'), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Literal('hello'), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 1 (String)', () => { type T = 'hello' extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal('hello'), Type.Object({}, { additionalProperties: false })) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Literal('hello'), Type.Object({}, { additionalProperties: false })) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Object 2 (String)', () => { type T = 'hello' extends { a: 10 } ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal('hello'), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Literal('hello'), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 1 (String)', () => { type T = 'hello' extends number | string ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal('hello'), Type.Union([Type.Number(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Literal('hello'), Type.Union([Type.Number(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 2 (String)', () => { type T = 'hello' extends any | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal('hello'), Type.Union([Type.Any(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Literal('hello'), Type.Union([Type.Any(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 3 (String)', () => { type T = 'hello' extends boolean | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal('hello'), Type.Union([Type.Boolean(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Literal('hello'), Type.Union([Type.Boolean(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Null (String)', () => { type T = 'hello' extends null ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal('hello'), Type.Null()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Literal('hello'), Type.Null()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Undefined (String)', () => { type T = 'hello' extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal('hello'), Type.Undefined()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Literal('hello'), Type.Undefined()) + Assert.IsEqual(R, ExtendsResult.False) }) // ------------------------------------------------------------------- // Number Literal // ------------------------------------------------------------------- it('Should extend Any (Number)', () => { type T = 10 extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(10), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Literal(10), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Unknown (Number)', () => { type T = 10 extends unknown ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(10), Type.Unknown()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Literal(10), Type.Unknown()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend String (Number)', () => { type T = 10 extends string ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(10), Type.String()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Literal(10), Type.String()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Boolean (Number)', () => { type T = 10 extends boolean ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(10), Type.Boolean()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Literal(10), Type.Boolean()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Number (Number)', () => { type T = 10 extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(10), Type.Number()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Literal(10), Type.Number()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Integer (Number)', () => { type T = 10 extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(10), Type.Integer()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Literal(10), Type.Integer()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Array (Number)', () => { type T = 10 extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(10), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Literal(10), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Tuple (Number)', () => { type T = 10 extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(10), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Literal(10), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 1 (Number)', () => { type T = 10 extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(10), Type.Object({}, { additionalProperties: false })) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Literal(10), Type.Object({}, { additionalProperties: false })) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Object 2 (Number)', () => { type T = 10 extends { a: 10 } ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(10), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Literal(10), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 1 (Number)', () => { type T = 10 extends number | string ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(10), Type.Union([Type.Number(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Literal(10), Type.Union([Type.Number(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 2 (Number)', () => { type T = 10 extends any | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(10), Type.Union([Type.Any(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Literal(10), Type.Union([Type.Any(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 3 (Number)', () => { type T = 10 extends boolean | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(10), Type.Union([Type.Boolean(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Literal(10), Type.Union([Type.Boolean(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Null (Number)', () => { type T = 10 extends null ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(10), Type.Null()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Literal(10), Type.Null()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Undefined (Number)', () => { type T = 10 extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(10), Type.Undefined()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Literal(10), Type.Undefined()) + Assert.IsEqual(R, ExtendsResult.False) }) // ------------------------------------------------------------------- // Boolean Literal // ------------------------------------------------------------------- it('Should extend Any (Boolean)', () => { type T = true extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(true), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Literal(true), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Unknown (Boolean)', () => { type T = true extends unknown ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(true), Type.Unknown()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Literal(true), Type.Unknown()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend String (Boolean)', () => { type T = true extends string ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(true), Type.String()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Literal(true), Type.String()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Boolean (Boolean)', () => { type T = true extends boolean ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(true), Type.Boolean()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Literal(true), Type.Boolean()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Number (Boolean)', () => { type T = true extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(true), Type.Number()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Literal(true), Type.Number()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Integer (Boolean)', () => { type T = true extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(true), Type.Integer()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Literal(true), Type.Integer()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array (Boolean)', () => { type T = true extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(true), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Literal(true), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Tuple (Boolean)', () => { type T = true extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(true), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Literal(true), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Record 1', () => { type T = 'hello' extends Record ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal('hello'), Type.Record(Type.Number(), Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Literal('hello'), Type.Record(Type.Number(), Type.Any())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Record 2', () => { type T = 10 extends Record ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(10), Type.Record(Type.Number(), Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Literal(10), Type.Record(Type.Number(), Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Record 3', () => { type T = true extends Record ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(true), Type.Record(Type.Number(), Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Literal(true), Type.Record(Type.Number(), Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 1 (Boolean)', () => { type T = true extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(true), Type.Object({}, { additionalProperties: false })) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Literal(true), Type.Object({}, { additionalProperties: false })) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Object 2 (Boolean)', () => { type T = true extends { a: 10 } ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(true), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Literal(true), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 1 (Boolean)', () => { type T = true extends number | string ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(true), Type.Union([Type.Number(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Literal(true), Type.Union([Type.Number(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 2 (Boolean)', () => { type T = true extends any | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(true), Type.Union([Type.Any(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Literal(true), Type.Union([Type.Any(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 3 (Boolean)', () => { type T = true extends boolean | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(true), Type.Union([Type.Boolean(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Literal(true), Type.Union([Type.Boolean(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Null (Boolean)', () => { type T = true extends null ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(true), Type.Null()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Literal(true), Type.Null()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Undefined (Boolean)', () => { type T = true extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(true), Type.Undefined()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Literal(true), Type.Undefined()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Void', () => { type T = true extends void ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(true), Type.Void()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Literal(true), Type.Void()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Date', () => { type T = true extends Date ? 1 : 2 - const R = TypeExtends.Extends(Type.Literal(true), Type.Date()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Literal(true), Type.Date()) + Assert.IsEqual(R, ExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/not.ts b/test/runtime/type/extends/not.ts index 53f8d1e40..fd3a97ab8 100644 --- a/test/runtime/type/extends/not.ts +++ b/test/runtime/type/extends/not.ts @@ -1,5 +1,4 @@ -import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import { Type, ExtendsCheck, ExtendsResult } from '@sinclair/typebox' import { Assert } from '../../assert/index' // --------------------------------------------------------------------------- @@ -18,8 +17,8 @@ describe('type/extends/Not', () => { it('Should extend with unknown assignability check', () => { const A = Type.Number() const B = Type.Not(Type.Number()) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.True) // we would expect false + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.True) // we would expect false }) // --------------------------------------------------------------------------- // Nested @@ -31,17 +30,17 @@ describe('type/extends/Not', () => { const T4 = Type.Not(T3) const T5 = Type.Not(T4) - const R1 = TypeExtends.Extends(T1, Type.String()) - const R2 = TypeExtends.Extends(T2, Type.String()) - const R3 = TypeExtends.Extends(T3, Type.String()) - const R4 = TypeExtends.Extends(T4, Type.String()) - const R5 = TypeExtends.Extends(T5, Type.String()) + const R1 = ExtendsCheck(T1, Type.String()) + const R2 = ExtendsCheck(T2, Type.String()) + const R3 = ExtendsCheck(T3, Type.String()) + const R4 = ExtendsCheck(T4, Type.String()) + const R5 = ExtendsCheck(T5, Type.String()) - Assert.IsEqual(R1, TypeExtendsResult.True) - Assert.IsEqual(R2, TypeExtendsResult.False) - Assert.IsEqual(R3, TypeExtendsResult.True) - Assert.IsEqual(R4, TypeExtendsResult.False) - Assert.IsEqual(R5, TypeExtendsResult.True) + Assert.IsEqual(R1, ExtendsResult.True) + Assert.IsEqual(R2, ExtendsResult.False) + Assert.IsEqual(R3, ExtendsResult.True) + Assert.IsEqual(R4, ExtendsResult.False) + Assert.IsEqual(R5, ExtendsResult.True) }) // --------------------------------------------------------------------------- @@ -49,97 +48,97 @@ describe('type/extends/Not', () => { // --------------------------------------------------------------------------- it('Should extend Any', () => { type T = unknown extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Not(Type.Number()), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Unknown', () => { type T = unknown extends unknown ? 1 : 2 - const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Unknown()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Not(Type.Number()), Type.Unknown()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend String', () => { type T = unknown extends string ? 1 : 2 - const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.String()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Not(Type.Number()), Type.String()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Boolean', () => { type T = unknown extends boolean ? 1 : 2 - const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Boolean()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Not(Type.Number()), Type.Boolean()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Number', () => { type T = unknown extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Number()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Not(Type.Number()), Type.Number()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Integer', () => { type T = unknown extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Integer()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Not(Type.Number()), Type.Integer()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array 1', () => { type T = unknown extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Not(Type.Number()), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array 2', () => { type T = unknown extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Array(Type.String())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Not(Type.Number()), Type.Array(Type.String())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Tuple', () => { type T = unknown extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Not(Type.Number()), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 1', () => { type T = unknown extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Object({})) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Not(Type.Number()), Type.Object({})) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 2', () => { type T = unknown extends { a: number } ? 1 : 2 - const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Object({ a: Type.Number() })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Not(Type.Number()), Type.Object({ a: Type.Number() })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 1', () => { type T = unknown extends number | string ? 1 : 2 - const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Union([Type.Number(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Not(Type.Number()), Type.Union([Type.Number(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 2', () => { type T = unknown extends any | number ? 1 : 2 // 1 - const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Union([Type.Any(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Not(Type.Number()), Type.Union([Type.Any(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 3', () => { type T = unknown extends unknown | number ? 1 : 2 // 1 - const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Union([Type.Unknown(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Not(Type.Number()), Type.Union([Type.Unknown(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 4', () => { type T = unknown extends unknown | any ? 1 : 2 // 1 - const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Union([Type.Unknown(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Not(Type.Number()), Type.Union([Type.Unknown(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Null', () => { type T = unknown extends null ? 1 : 2 - const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Null()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Not(Type.Number()), Type.Null()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Undefined', () => { type T = unknown extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Undefined()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Not(Type.Number()), Type.Undefined()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Void', () => { type T = unknown extends void ? 1 : 2 - const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Void()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Not(Type.Number()), Type.Void()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Date', () => { type T = unknown extends Date ? 1 : 2 - const R = TypeExtends.Extends(Type.Not(Type.Number()), Type.Date()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Not(Type.Number()), Type.Date()) + Assert.IsEqual(R, ExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/null.ts b/test/runtime/type/extends/null.ts index 702e16271..157d7d264 100644 --- a/test/runtime/type/extends/null.ts +++ b/test/runtime/type/extends/null.ts @@ -1,101 +1,100 @@ -import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import { Type, ExtendsCheck, ExtendsResult } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/extends/Null', () => { it('Should extend Any', () => { type T = null extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.Null(), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Null(), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Unknown', () => { type T = null extends unknown ? 1 : 2 - const R = TypeExtends.Extends(Type.Null(), Type.Unknown()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Null(), Type.Unknown()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend String', () => { type T = null extends string ? 1 : 2 - const R = TypeExtends.Extends(Type.Null(), Type.String()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Null(), Type.String()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Boolean', () => { type T = null extends boolean ? 1 : 2 - const R = TypeExtends.Extends(Type.Null(), Type.Boolean()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Null(), Type.Boolean()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Number', () => { type T = null extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Null(), Type.Number()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Null(), Type.Number()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Integer', () => { type T = null extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Null(), Type.Integer()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Null(), Type.Integer()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array', () => { type T = null extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Null(), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Null(), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Tuple', () => { type T = null extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.Null(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Null(), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Record', () => { type T = null extends Record ? 1 : 2 - const R = TypeExtends.Extends(Type.Null(), Type.Record(Type.Number(), Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Null(), Type.Record(Type.Number(), Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 1', () => { type T = null extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.Null(), Type.Object({}, { additionalProperties: false })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Null(), Type.Object({}, { additionalProperties: false })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 2', () => { type T = null extends { a: 10 } ? 1 : 2 - const R = TypeExtends.Extends(Type.Null(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Null(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 3', () => { type T = null extends object ? 1 : 2 - const R = TypeExtends.Extends(Type.Null(), Type.Object({})) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Null(), Type.Object({})) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 1', () => { type T = null extends number | string ? 1 : 2 - const R = TypeExtends.Extends(Type.Null(), Type.Union([Type.Number(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Null(), Type.Union([Type.Number(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 2', () => { type T = null extends any | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Null(), Type.Union([Type.Any(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Null(), Type.Union([Type.Any(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 2', () => { type T = null extends boolean | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Null(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Null(), Type.Union([Type.Boolean(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Null', () => { type T = null extends null ? 1 : 2 - const R = TypeExtends.Extends(Type.Null(), Type.Null()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Null(), Type.Null()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Undefined', () => { type T = null extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.Null(), Type.Undefined()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Null(), Type.Undefined()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Void', () => { type T = null extends void ? 1 : 2 - const R = TypeExtends.Extends(Type.Null(), Type.Void()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Null(), Type.Void()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Date', () => { type T = null extends Date ? 1 : 2 - const R = TypeExtends.Extends(Type.Null(), Type.Date()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Null(), Type.Date()) + Assert.IsEqual(R, ExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/number.ts b/test/runtime/type/extends/number.ts index aa647eca6..ce9c4189d 100644 --- a/test/runtime/type/extends/number.ts +++ b/test/runtime/type/extends/number.ts @@ -1,96 +1,95 @@ -import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import { Type, ExtendsCheck, ExtendsResult } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/extends/Number', () => { it('Should extend Any', () => { type T = number extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.Number(), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Number(), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Unknown', () => { type T = number extends unknown ? 1 : 2 - const R = TypeExtends.Extends(Type.Number(), Type.Unknown()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Number(), Type.Unknown()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend String', () => { type T = number extends string ? 1 : 2 - const R = TypeExtends.Extends(Type.Number(), Type.String()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Number(), Type.String()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Boolean', () => { type T = number extends boolean ? 1 : 2 - const R = TypeExtends.Extends(Type.Number(), Type.Boolean()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Number(), Type.Boolean()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Number', () => { type T = number extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Number(), Type.Number()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Number(), Type.Number()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Integer', () => { type T = number extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Number(), Type.Integer()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Number(), Type.Integer()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Array', () => { type T = number extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Number(), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Number(), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Tuple', () => { type T = number extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.Number(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Number(), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Record', () => { type T = number extends Record ? 1 : 2 - const R = TypeExtends.Extends(Type.Number(), Type.Record(Type.Number(), Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Number(), Type.Record(Type.Number(), Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 1', () => { type T = number extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.Number(), Type.Object({}, { additionalProperties: false })) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Number(), Type.Object({}, { additionalProperties: false })) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Object 2', () => { type T = number extends { a: 10 } ? 1 : 2 - const R = TypeExtends.Extends(Type.Number(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Number(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 1', () => { type T = number extends number | string ? 1 : 2 - const R = TypeExtends.Extends(Type.Number(), Type.Union([Type.Number(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Number(), Type.Union([Type.Number(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 2', () => { type T = number extends any | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Number(), Type.Union([Type.Any(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Number(), Type.Union([Type.Any(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 2', () => { type T = number extends boolean | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Number(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Number(), Type.Union([Type.Boolean(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Null', () => { type T = number extends null ? 1 : 2 - const R = TypeExtends.Extends(Type.Number(), Type.Null()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Number(), Type.Null()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Undefined', () => { type T = number extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.Number(), Type.Undefined()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Number(), Type.Undefined()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Void', () => { type T = number extends void ? 1 : 2 - const R = TypeExtends.Extends(Type.Number(), Type.Void()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Number(), Type.Void()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Date', () => { type T = number extends Date ? 1 : 2 - const R = TypeExtends.Extends(Type.Number(), Type.Date()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Number(), Type.Date()) + Assert.IsEqual(R, ExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/object.ts b/test/runtime/type/extends/object.ts index 8bc3a6e63..76e405818 100644 --- a/test/runtime/type/extends/object.ts +++ b/test/runtime/type/extends/object.ts @@ -1,5 +1,4 @@ -import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import { Type, ExtendsCheck, ExtendsResult } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/extends/Object', () => { @@ -10,43 +9,43 @@ describe('type/extends/Object', () => { type T = { x: number; y: number } extends { x: number } ? 1 : 2 const A = Type.Object({ x: Type.Number(), y: Type.Number() }) const B = Type.Object({ x: Type.Number() }) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Object 2', () => { type T = { x: number } extends { x: number; y: number } ? 1 : 2 const A = Type.Object({ x: Type.Number() }) const B = Type.Object({ x: Type.Number(), y: Type.Number() }) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 3', () => { type T = { x: number; y: string } extends { x: number } ? 1 : 2 const A = Type.Object({ x: Type.Number(), y: Type.String() }) const B = Type.Object({ x: Type.Number() }) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Object 4', () => { type T = { x: number } extends { x: number; y: string } ? 1 : 2 const A = Type.Object({ x: Type.Number() }) const B = Type.Object({ x: Type.Number(), y: Type.String() }) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 5', () => { type T = { x: number | string } extends { x: number } ? 1 : 2 const A = Type.Object({ x: Type.Union([Type.Number(), Type.String()]) }) const B = Type.Object({ x: Type.Number() }) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 6', () => { type T = { x: number } extends { x: number | string } ? 1 : 2 const A = Type.Object({ x: Type.Number() }) const B = Type.Object({ x: Type.Union([Type.Number(), Type.String()]) }) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.True) }) // ---------------------------------------------------------- // Record @@ -55,124 +54,124 @@ describe('type/extends/Object', () => { type T = { a: number; b: number } extends Record ? 1 : 2 const A = Type.Object({ a: Type.Number(), b: Type.Number() }) const B = Type.Record(Type.String(), Type.Number()) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Record 3', () => { type T = { a: number; b: number } extends Record ? 1 : 2 const A = Type.Object({ a: Type.Number(), b: Type.Number() }) const B = Type.Record(Type.Number(), Type.Number()) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Record 4', () => { type T = { a: number; b: number } extends Record<'a' | 'b', number> ? 1 : 2 const A = Type.Object({ a: Type.Number(), b: Type.Number() }) const B = Type.Record(Type.Union([Type.Literal('a'), Type.Literal('b')]), Type.Number()) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Record 5', () => { type T = { a: number; b: number } extends Record<'a' | 'b', number> ? 1 : 2 const A = Type.Object({ a: Type.Number(), b: Type.Number() }) const B = Type.Record(Type.String(), Type.Number()) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Record 6', () => { type T = { a: number; b: number } extends Record<'a' | 'b', number> ? 1 : 2 const A = Type.Object({ a: Type.Number(), b: Type.Number() }) const B = Type.Record(Type.Number(), Type.Number()) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.True) }) // ---------------------------------------------------------- // Standard // ---------------------------------------------------------- it('Should extend Any', () => { type T = { a: number } extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Object({ a: Type.Number() }), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend String', () => { type T = { a: number } extends string ? 1 : 2 - const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.String()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Object({ a: Type.Number() }), Type.String()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Boolean', () => { type T = { a: number } extends boolean ? 1 : 2 - const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Boolean()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Object({ a: Type.Number() }), Type.Boolean()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Number', () => { type T = { a: number } extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Number()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Object({ a: Type.Number() }), Type.Number()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Integer', () => { type T = { a: number } extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Integer()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Object({ a: Type.Number() }), Type.Integer()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array', () => { type T = { a: number } extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Object({ a: Type.Number() }), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Tuple', () => { type T = { a: number } extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Object({ a: Type.Number() }), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 1', () => { type T = { a: number } extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Object({}, { additionalProperties: false })) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Object({ a: Type.Number() }), Type.Object({}, { additionalProperties: false })) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Object 2', () => { type T = { a: number } extends { a: 10 } ? 1 : 2 - const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Object({ a: Type.Literal(10) })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Object({ a: Type.Number() }), Type.Object({ a: Type.Literal(10) })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 3', () => { type T = { a: number } extends object ? 1 : 2 - const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Object({})) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Object({ a: Type.Number() }), Type.Object({})) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 1', () => { type T = { a: number } extends number | string ? 1 : 2 - const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Union([Type.Null(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Object({ a: Type.Number() }), Type.Union([Type.Null(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 2', () => { type T = { a: number } extends any | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Union([Type.Any(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Object({ a: Type.Number() }), Type.Union([Type.Any(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 2', () => { type T = { a: number } extends boolean | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Union([Type.Boolean(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Object({ a: Type.Number() }), Type.Union([Type.Boolean(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Null', () => { type T = { a: number } extends null ? 1 : 2 - const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Null()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Object({ a: Type.Number() }), Type.Null()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Undefined', () => { type T = { a: number } extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Undefined()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Object({ a: Type.Number() }), Type.Undefined()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Void', () => { type T = { a: number } extends void ? 1 : 2 - const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Void()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Object({ a: Type.Number() }), Type.Void()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Date', () => { type T = { a: number } extends Date ? 1 : 2 - const R = TypeExtends.Extends(Type.Object({ a: Type.Number() }), Type.Date()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Object({ a: Type.Number() }), Type.Date()) + Assert.IsEqual(R, ExtendsResult.False) }) // ---------------------------------------------------------------- // Optional @@ -180,14 +179,14 @@ describe('type/extends/Object', () => { it('Should extend optional 1', () => { const A = Type.Object({ a: Type.Number() }) const B = Type.Object({ a: Type.Optional(Type.Number()) }) - const C = TypeExtends.Extends(A, B) - Assert.IsEqual(C, TypeExtendsResult.True) + const C = ExtendsCheck(A, B) + Assert.IsEqual(C, ExtendsResult.True) }) it('Should extend optional 2', () => { const A = Type.Object({ a: Type.Number() }) const B = Type.Object({ a: Type.Optional(Type.Number()) }) - const C = TypeExtends.Extends(B, A) - Assert.IsEqual(C, TypeExtendsResult.False) + const C = ExtendsCheck(B, A) + Assert.IsEqual(C, ExtendsResult.False) }) it('Should extend optional 3', () => { const A = Type.Object({ @@ -198,8 +197,8 @@ describe('type/extends/Object', () => { y: Type.Number(), z: Type.Number(), }) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend optional 4', () => { const A = Type.Object({ @@ -210,8 +209,8 @@ describe('type/extends/Object', () => { y: Type.Number(), z: Type.Number(), }) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend optional 5', () => { const A = Type.Object({ @@ -222,7 +221,7 @@ describe('type/extends/Object', () => { y: Type.Number(), z: Type.Optional(Type.Number()), }) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.True) }) }) diff --git a/test/runtime/type/extends/promise.ts b/test/runtime/type/extends/promise.ts index 843786a64..2313c8738 100644 --- a/test/runtime/type/extends/promise.ts +++ b/test/runtime/type/extends/promise.ts @@ -1,5 +1,4 @@ -import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import { Type, ExtendsCheck, ExtendsResult } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/extends/Promise', () => { @@ -8,203 +7,203 @@ describe('type/extends/Promise', () => { // ---------------------------------------------- it('Should extend Promise Varying 1', () => { type T = Promise extends Promise ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Promise(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Promise(Type.Any()), Type.Promise(Type.Any())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Promise Varying 2', () => { type T = Promise extends Promise ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.String()), Type.Promise(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Promise(Type.String()), Type.Promise(Type.Any())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Promise Varying 3', () => { type T = Promise extends Promise ? 1 : 2 // 1 - const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Promise(Type.String())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Promise(Type.Any()), Type.Promise(Type.String())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Promise Varying 4', () => { type T = Promise extends Promise ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Promise(Type.String())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Promise(Type.Number()), Type.Promise(Type.String())) + Assert.IsEqual(R, ExtendsResult.False) }) // ---------------------------------------------- // Any // ---------------------------------------------- it('Should extend Any', () => { type T = Promise extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Promise(Type.Any()), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Unknown', () => { type T = Promise extends unknown ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Unknown()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Promise(Type.Any()), Type.Unknown()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend String', () => { type T = Promise extends string ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.String()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Promise(Type.Any()), Type.String()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Boolean', () => { type T = Promise extends boolean ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Boolean()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Promise(Type.Any()), Type.Boolean()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Number', () => { type T = Promise extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Number()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Promise(Type.Any()), Type.Number()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Integer', () => { type T = Promise extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Integer()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Promise(Type.Any()), Type.Integer()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array', () => { type T = Promise extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Promise(Type.Any()), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Tuple', () => { type T = Promise extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Promise(Type.Any()), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Record', () => { type T = Promise extends Record ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Record(Type.Number(), Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Promise(Type.Any()), Type.Record(Type.Number(), Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 1', () => { type T = Promise extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Object({}, { additionalProperties: false })) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Promise(Type.Any()), Type.Object({}, { additionalProperties: false })) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Object 2', () => { type T = Promise extends { a: 10 } ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Promise(Type.Any()), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 3', () => { type T = Promise extends object ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Object({})) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Promise(Type.Any()), Type.Object({})) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 1', () => { type T = Promise extends number | string ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Union([Type.Number(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Promise(Type.Any()), Type.Union([Type.Number(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 2', () => { type T = Promise extends any | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Union([Type.Any(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Promise(Type.Any()), Type.Union([Type.Any(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 2', () => { type T = Promise extends boolean | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Union([Type.Boolean(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Promise(Type.Any()), Type.Union([Type.Boolean(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Null', () => { type T = Promise extends null ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Null()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Promise(Type.Any()), Type.Null()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Undefined', () => { type T = Promise extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Undefined()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Promise(Type.Any()), Type.Undefined()) + Assert.IsEqual(R, ExtendsResult.False) }) // ---------------------------------------------- // Constrained // ---------------------------------------------- it('Should extend constrained Any', () => { type T = Promise extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Promise(Type.Number()), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend constrained Unknown', () => { type T = Promise extends unknown ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Unknown()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Promise(Type.Number()), Type.Unknown()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend constrained String', () => { type T = Promise extends string ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.String()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Promise(Type.Number()), Type.String()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend constrained Boolean', () => { type T = Promise extends boolean ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Boolean()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Promise(Type.Number()), Type.Boolean()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend constrained Number', () => { type T = Promise extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Any()), Type.Number()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Promise(Type.Any()), Type.Number()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend constrained Integer', () => { type T = Promise extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Integer()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Promise(Type.Number()), Type.Integer()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend constrained Array', () => { type T = Promise extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Promise(Type.Number()), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend constrained Tuple', () => { type T = Promise extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Promise(Type.Number()), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend constrained Object 1', () => { type T = Promise extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Object({}, { additionalProperties: false })) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Promise(Type.Number()), Type.Object({}, { additionalProperties: false })) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend constrained Object 2', () => { type T = Promise extends { a: 10 } ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Promise(Type.Number()), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend constrained Object 3', () => { type T = Promise extends object ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Object({})) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Promise(Type.Number()), Type.Object({})) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend constrained Union 1', () => { type T = Promise extends number | string ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Union([Type.Number(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Promise(Type.Number()), Type.Union([Type.Number(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend constrained Union 2', () => { type T = Promise extends any | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Union([Type.Any(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Promise(Type.Number()), Type.Union([Type.Any(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend constrained Union 2', () => { type T = Promise extends boolean | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Union([Type.Boolean(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Promise(Type.Number()), Type.Union([Type.Boolean(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend constrained Null', () => { type T = Promise extends null ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Null()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Promise(Type.Number()), Type.Null()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend constrained Undefined', () => { type T = Promise extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Undefined()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Promise(Type.Number()), Type.Undefined()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Void', () => { type T = Promise extends void ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Void()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Promise(Type.Number()), Type.Void()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Date', () => { type T = Promise extends Date ? 1 : 2 - const R = TypeExtends.Extends(Type.Promise(Type.Number()), Type.Date()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Promise(Type.Number()), Type.Date()) + Assert.IsEqual(R, ExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/record.ts b/test/runtime/type/extends/record.ts index 7ac3301c2..905a96fcf 100644 --- a/test/runtime/type/extends/record.ts +++ b/test/runtime/type/extends/record.ts @@ -1,5 +1,4 @@ -import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import { Type, ExtendsCheck, ExtendsResult } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/extends/Record', () => { @@ -7,194 +6,194 @@ describe('type/extends/Record', () => { type T = Record<'a' | 'b', number> extends { a: number; b: number } ? 1 : 2 const A = Type.Record(Type.Union([Type.Literal('a'), Type.Literal('b')]), Type.Number()) const B = Type.Object({ a: Type.Number(), b: Type.Number() }) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Record 2', () => { type T = Record extends { a: number; b: number } ? 1 : 2 const A = Type.Record(Type.String(), Type.Number()) const B = Type.Object({ a: Type.Number(), b: Type.Number() }) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Record 3', () => { type T = Record extends { a: number; b: number } ? 1 : 2 const A = Type.Record(Type.Number(), Type.Number()) const B = Type.Object({ a: Type.Number(), b: Type.Number() }) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Record 4', () => { type T = Record<'a' | 'b', number> extends { a: number; b: number } ? 1 : 2 const A = Type.Record(Type.Union([Type.Literal('a'), Type.Literal('b')]), Type.Number()) const B = Type.Object({ a: Type.Number(), b: Type.Number() }) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Record 5', () => { type T = Record<'a' | 'b', number> extends { a: number; b: number } ? 1 : 2 const A = Type.Record(Type.Union([Type.Literal('a'), Type.Literal('b')]), Type.Number()) const B = Type.Object({ a: Type.Number(), b: Type.Number() }) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Record 6', () => { type T = Record extends Record<'a' | 'b', number> ? true : false const A = Type.Record(Type.String(), Type.Number()) const B = Type.Record(Type.Union([Type.Literal('a'), Type.Literal('b')]), Type.Number()) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Record 7', () => { type T = Record extends Record ? 1 : 2 const A = Type.Record(Type.String(), Type.Number()) const B = Type.Record(Type.String(), Type.Number()) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Record 8', () => { type T = Record extends Record ? 1 : 2 const A = Type.Record(Type.String(), Type.Number()) const B = Type.Record(Type.Number(), Type.Number()) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Record 9', () => { type T = Record extends Record<'a' | 'b', number> ? 1 : 2 const A = Type.Record(Type.Number(), Type.Number()) const B = Type.Record(Type.Union([Type.Literal('a'), Type.Literal('b')]), Type.Number()) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Record 10', () => { type T = Record extends Record ? 1 : 2 const A = Type.Record(Type.Number(), Type.Number()) const B = Type.Record(Type.String(), Type.Number()) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Record 11', () => { type T = Record extends Record ? 1 : 2 const A = Type.Record(Type.Number(), Type.Number()) const B = Type.Record(Type.Number(), Type.Number()) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.True) }) // ----- it('Should extend Record 12', () => { type T = Record extends Record ? 1 : 2 const A = Type.Record(Type.String(), Type.Number()) const B = Type.Record(Type.Integer(), Type.Number()) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Record 13', () => { type T = Record extends Record<'a' | 'b', number> ? 1 : 2 const A = Type.Record(Type.Integer(), Type.Number()) const B = Type.Record(Type.Union([Type.Literal('a'), Type.Literal('b')]), Type.Number()) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Record 14', () => { type T = Record extends Record ? 1 : 2 const A = Type.Record(Type.Integer(), Type.Number()) const B = Type.Record(Type.String(), Type.Number()) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Record 15', () => { type T = Record extends Record ? 1 : 2 const A = Type.Record(Type.Integer(), Type.Number()) const B = Type.Record(Type.Integer(), Type.Number()) - const R = TypeExtends.Extends(A, B) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(A, B) + Assert.IsEqual(R, ExtendsResult.True) }) // ------------------------------------------------------------------- // Standard // ------------------------------------------------------------------- it('Should extend Any', () => { type T = Record extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Record(Type.Number(), Type.Number()), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Unknown', () => { type T = Record extends unknown ? 1 : 2 - const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Unknown()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Record(Type.Number(), Type.Number()), Type.Unknown()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend String', () => { type T = Record extends string ? 1 : 2 - const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.String()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Record(Type.Number(), Type.Number()), Type.String()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Boolean', () => { type T = Record extends boolean ? 1 : 2 - const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Boolean()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Record(Type.Number(), Type.Number()), Type.Boolean()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Number', () => { type T = Record extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Number()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Record(Type.Number(), Type.Number()), Type.Number()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Integer', () => { type T = Record extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Integer()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Record(Type.Number(), Type.Number()), Type.Integer()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array 1', () => { type T = Record extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Record(Type.Number(), Type.Number()), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array 2', () => { type T = Record extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Array(Type.String())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Record(Type.Number(), Type.Number()), Type.Array(Type.String())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Tuple', () => { type T = Record extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Record(Type.Number(), Type.Number()), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 2', () => { type T = Record extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Object({})) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Record(Type.Number(), Type.Number()), Type.Object({})) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Object 3', () => { type T = Record extends { a: number } ? 1 : 2 - const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Object({ a: Type.Number() })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Record(Type.Number(), Type.Number()), Type.Object({ a: Type.Number() })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 1', () => { type T = Record extends number | string ? 1 : 2 - const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Union([Type.Number(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Record(Type.Number(), Type.Number()), Type.Union([Type.Number(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 2', () => { type T = Record extends any | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Union([Type.Any(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Record(Type.Number(), Type.Number()), Type.Union([Type.Any(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Null', () => { type T = Record extends null ? 1 : 2 - const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Null()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Record(Type.Number(), Type.Number()), Type.Null()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Undefined', () => { type T = Record extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Undefined()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Record(Type.Number(), Type.Number()), Type.Undefined()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Void', () => { type T = Record extends void ? 1 : 2 - const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Void()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Record(Type.Number(), Type.Number()), Type.Void()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Date', () => { type T = Record extends Date ? 1 : 2 - const R = TypeExtends.Extends(Type.Record(Type.Number(), Type.Number()), Type.Date()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Record(Type.Number(), Type.Number()), Type.Date()) + Assert.IsEqual(R, ExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/string.ts b/test/runtime/type/extends/string.ts index 0dd95ecbe..cdb3e216c 100644 --- a/test/runtime/type/extends/string.ts +++ b/test/runtime/type/extends/string.ts @@ -1,111 +1,110 @@ -import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import { Type, ExtendsCheck, ExtendsResult } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/extends/String', () => { it('Should extend Any', () => { type T = string extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.String(), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.String(), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Unknown', () => { type T = string extends unknown ? 1 : 2 - const R = TypeExtends.Extends(Type.String(), Type.Unknown()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.String(), Type.Unknown()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend String', () => { type T = string extends string ? 1 : 2 - const R = TypeExtends.Extends(Type.String(), Type.String()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.String(), Type.String()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Boolean', () => { type T = string extends boolean ? 1 : 2 - const R = TypeExtends.Extends(Type.String(), Type.Boolean()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.String(), Type.Boolean()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Number', () => { type T = string extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.String(), Type.Number()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.String(), Type.Number()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Integer', () => { type T = string extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.String(), Type.Integer()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.String(), Type.Integer()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array', () => { type T = string extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.String(), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.String(), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Tuple', () => { type T = string extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.String(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.String(), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Record 1', () => { type T = string extends Record ? 1 : 2 - const R = TypeExtends.Extends(Type.String(), Type.Record(Type.Number(), Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.String(), Type.Record(Type.Number(), Type.Any())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Record 2', () => { type T = string extends Record ? 1 : 2 - const R = TypeExtends.Extends(Type.String(), Type.Record(Type.Number(), Type.Unknown())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.String(), Type.Record(Type.Number(), Type.Unknown())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Record 3', () => { type T = string extends Record ? 1 : 2 - const R = TypeExtends.Extends(Type.String(), Type.Record(Type.Number(), Type.String())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.String(), Type.Record(Type.Number(), Type.String())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Record 4', () => { type T = string extends Record ? 1 : 2 - const R = TypeExtends.Extends(Type.String(), Type.Record(Type.Number(), Type.Number())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.String(), Type.Record(Type.Number(), Type.Number())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 1', () => { type T = string extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.String(), Type.Object({}, { additionalProperties: false })) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.String(), Type.Object({}, { additionalProperties: false })) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Object 2', () => { type T = string extends { a: 10 } ? 1 : 2 - const R = TypeExtends.Extends(Type.String(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.String(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 1', () => { type T = number extends number | string ? 1 : 2 - const R = TypeExtends.Extends(Type.Number(), Type.Union([Type.Number(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Number(), Type.Union([Type.Number(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 2', () => { type T = number extends any | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Number(), Type.Union([Type.Any(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Number(), Type.Union([Type.Any(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 3', () => { type T = number extends boolean | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Number(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Number(), Type.Union([Type.Boolean(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Null', () => { type T = number extends null ? 1 : 2 - const R = TypeExtends.Extends(Type.Number(), Type.Null()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Number(), Type.Null()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Undefined', () => { type T = number extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.Number(), Type.Undefined()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Number(), Type.Undefined()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Void', () => { type T = number extends void ? 1 : 2 - const R = TypeExtends.Extends(Type.Number(), Type.Void()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Number(), Type.Void()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Date', () => { type T = number extends Date ? 1 : 2 - const R = TypeExtends.Extends(Type.Number(), Type.Date()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Number(), Type.Date()) + Assert.IsEqual(R, ExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/symbol.ts b/test/runtime/type/extends/symbol.ts index 03dc7aaf0..9412f4fc8 100644 --- a/test/runtime/type/extends/symbol.ts +++ b/test/runtime/type/extends/symbol.ts @@ -1,106 +1,105 @@ -import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import { Type, ExtendsCheck, ExtendsResult } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/extends/Symbol', () => { it('Should extend Any', () => { type T = symbol extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.Symbol(), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Symbol(), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Unknown', () => { type T = symbol extends unknown ? 1 : 2 - const R = TypeExtends.Extends(Type.Symbol(), Type.Unknown()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Symbol(), Type.Unknown()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend String', () => { type T = symbol extends string ? 1 : 2 - const R = TypeExtends.Extends(Type.Symbol(), Type.String()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Symbol(), Type.String()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Boolean', () => { type T = symbol extends boolean ? 1 : 2 - const R = TypeExtends.Extends(Type.Symbol(), Type.Boolean()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Symbol(), Type.Boolean()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Number', () => { type T = symbol extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Symbol(), Type.Number()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Symbol(), Type.Number()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Integer', () => { type T = symbol extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Symbol(), Type.Integer()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Symbol(), Type.Integer()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array', () => { type T = symbol extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Symbol(), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Symbol(), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Tuple', () => { type T = symbol extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.Symbol(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Symbol(), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Record', () => { type T = symbol extends Record ? 1 : 2 - const R = TypeExtends.Extends(Type.Symbol(), Type.Record(Type.Number(), Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Symbol(), Type.Record(Type.Number(), Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 1', () => { type T = symbol extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.Symbol(), Type.Object({})) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Symbol(), Type.Object({})) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Object 2', () => { type T = symbol extends { a: 10 } ? 1 : 2 - const R = TypeExtends.Extends(Type.Symbol(), Type.Object({ a: Type.Literal(10) })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Symbol(), Type.Object({ a: Type.Literal(10) })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 1', () => { type T = symbol extends number | string ? 1 : 2 - const R = TypeExtends.Extends(Type.Symbol(), Type.Union([Type.Number(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Symbol(), Type.Union([Type.Number(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 2', () => { type T = symbol extends any | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Symbol(), Type.Union([Type.Any(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Symbol(), Type.Union([Type.Any(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 3', () => { type T = symbol extends boolean | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Symbol(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Symbol(), Type.Union([Type.Boolean(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 4', () => { type T = symbol extends boolean | symbol ? 1 : 2 - const R = TypeExtends.Extends(Type.Symbol(), Type.Union([Type.Boolean(), Type.Symbol()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Symbol(), Type.Union([Type.Boolean(), Type.Symbol()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Null', () => { type T = symbol extends null ? 1 : 2 - const R = TypeExtends.Extends(Type.Symbol(), Type.Null()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Symbol(), Type.Null()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Undefined', () => { type T = symbol extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.Symbol(), Type.Undefined()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Symbol(), Type.Undefined()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Void', () => { type T = symbol extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.Symbol(), Type.Void()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Symbol(), Type.Void()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Date', () => { type T = symbol extends Date ? 1 : 2 - const R = TypeExtends.Extends(Type.Symbol(), Type.Date()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Symbol(), Type.Date()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Symbol', () => { type T = symbol extends symbol ? 1 : 2 - const R = TypeExtends.Extends(Type.Symbol(), Type.Symbol()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Symbol(), Type.Symbol()) + Assert.IsEqual(R, ExtendsResult.True) }) }) diff --git a/test/runtime/type/extends/template-literal.ts b/test/runtime/type/extends/template-literal.ts index 3dd6ff3d2..1e8aeab12 100644 --- a/test/runtime/type/extends/template-literal.ts +++ b/test/runtime/type/extends/template-literal.ts @@ -1,5 +1,4 @@ -import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import { Type, ExtendsCheck, ExtendsResult } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/extends/TemplateLiteral', () => { @@ -8,155 +7,155 @@ describe('type/extends/TemplateLiteral', () => { // ------------------------------------------------------------------- it('Should extend Any (hello)', () => { type T = 'hello' extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Literal('hello')]), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Unknown (hello)', () => { type T = 'hello' extends unknown ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Unknown()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Literal('hello')]), Type.Unknown()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend String (hello)', () => { type T = 'hello' extends string ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.String()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Literal('hello')]), Type.String()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Boolean (hello)', () => { type T = 'hello' extends boolean ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Boolean()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Literal('hello')]), Type.Boolean()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Number (hello)', () => { type T = 'hello' extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Number()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Literal('hello')]), Type.Number()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Integer (hello)', () => { type T = 'hello' extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Integer()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Literal('hello')]), Type.Integer()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array (hello)', () => { type T = 'hello' extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Literal('hello')]), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Tuple (hello)', () => { type T = 'hello' extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Literal('hello')]), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 1 (hello)', () => { type T = 'hello' extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Object({}, { additionalProperties: false })) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Literal('hello')]), Type.Object({}, { additionalProperties: false })) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Object 2 (hello)', () => { type T = 'hello' extends { a: 10 } ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Literal('hello')]), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 1 (hello)', () => { type T = 'hello' extends number | string ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Union([Type.Number(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Literal('hello')]), Type.Union([Type.Number(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 2 (hello)', () => { type T = 'hello' extends any | number ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Union([Type.Any(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Literal('hello')]), Type.Union([Type.Any(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 3 (hello)', () => { type T = 'hello' extends boolean | number ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Union([Type.Boolean(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Literal('hello')]), Type.Union([Type.Boolean(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Null (hello)', () => { type T = 'hello' extends null ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Null()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Literal('hello')]), Type.Null()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Undefined (hello)', () => { type T = 'hello' extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Literal('hello')]), Type.Undefined()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Literal('hello')]), Type.Undefined()) + Assert.IsEqual(R, ExtendsResult.False) }) // ------------------------------------------------------------------- // String Literal 'hello' | 'world' // ------------------------------------------------------------------- it('Should extend Any (hello | world)', () => { type T = 'hello' | 'world' extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Unknown (hello | world)', () => { type T = 'hello' | 'world' extends unknown ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Unknown()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Unknown()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend String (hello | world)', () => { type T = 'hello' | 'world' extends string ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.String()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.String()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Boolean (hello | world)', () => { type T = 'hello' | 'world' extends boolean ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Boolean()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Boolean()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Number (hello | world)', () => { type T = 'hello' | 'world' extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Number()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Number()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Integer (hello | world)', () => { type T = 'hello' | 'world' extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Integer()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Integer()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array (hello | world)', () => { type T = 'hello' | 'world' extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Tuple (hello | world)', () => { type T = 'hello' | 'world' extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 1 (hello | world)', () => { type T = 'hello' | 'world' extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Object({}, { additionalProperties: false })) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Object({}, { additionalProperties: false })) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Object 2 (hello | world)', () => { type T = 'hello' | 'world' extends { a: 10 } ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 1 (hello | world)', () => { type T = 'hello' | 'world' extends number | string ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Union([Type.Number(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Union([Type.Number(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 2 (hello | world)', () => { type T = 'hello' | 'world' extends any | number ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Union([Type.Any(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Union([Type.Any(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 3 (hello | world)', () => { type T = 'hello' | 'world' extends boolean | number ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Union([Type.Boolean(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Union([Type.Boolean(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Null (hello | world)', () => { type T = 'hello' | 'world' extends null ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Null()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Null()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Undefined (hello | world)', () => { type T = 'hello' | 'world' extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Undefined()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.TemplateLiteral([Type.Union([Type.Literal('hello'), Type.Literal('world')])]), Type.Undefined()) + Assert.IsEqual(R, ExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/tuple.ts b/test/runtime/type/extends/tuple.ts index b297655ab..99bc36033 100644 --- a/test/runtime/type/extends/tuple.ts +++ b/test/runtime/type/extends/tuple.ts @@ -1,161 +1,160 @@ -import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import { Type, ExtendsCheck, ExtendsResult } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/extends/Tuple', () => { it('Should extend Any', () => { type T = [string, number] extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Tuple([Type.String(), Type.Number()]), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend String', () => { type T = [string, number] extends string ? 1 : 2 - const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.String()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Tuple([Type.String(), Type.Number()]), Type.String()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Boolean', () => { type T = [string, number] extends boolean ? 1 : 2 - const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Boolean()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Tuple([Type.String(), Type.Number()]), Type.Boolean()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Number', () => { type T = [string, number] extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Number()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Tuple([Type.String(), Type.Number()]), Type.Number()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Integer', () => { type T = [string, number] extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Integer()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Tuple([Type.String(), Type.Number()]), Type.Integer()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array 1', () => { type T = [string, number] extends Array ? 1 : 2 // 1 - const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Tuple([Type.String(), Type.Number()]), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Array 2', () => { type T = [string, number] extends Array ? 1 : 2 // 2 - const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Array(Type.String())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Tuple([Type.String(), Type.Number()]), Type.Array(Type.String())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array 3', () => { type T = [string, number] extends Array ? 1 : 2 // 1 - const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Array(Type.Union([Type.String(), Type.Number()]))) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Tuple([Type.String(), Type.Number()]), Type.Array(Type.Union([Type.String(), Type.Number()]))) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Array 4', () => { type T = [string, number] extends Array ? 1 : 2 // 1 - const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Array(Type.Union([Type.String(), Type.Number(), Type.Boolean()]))) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Tuple([Type.String(), Type.Number()]), Type.Array(Type.Union([Type.String(), Type.Number(), Type.Boolean()]))) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Tuple 1', () => { type T = [string, number] extends [string, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Tuple([Type.String(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Tuple([Type.String(), Type.Number()]), Type.Tuple([Type.String(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Tuple 2', () => { type T = [string, number] extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Tuple([Type.String(), Type.Number()]), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Tuple 3', () => { type T = [string, any] extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Any()]), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Tuple([Type.String(), Type.Any()]), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Tuple 4', () => { type T = [string, number] extends [string, any] ? 1 : 2 - const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Tuple([Type.String(), Type.Any()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Tuple([Type.String(), Type.Number()]), Type.Tuple([Type.String(), Type.Any()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Tuple 5', () => { type T = [string, unknown] extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Unknown()]), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Tuple([Type.String(), Type.Unknown()]), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Tuple 6', () => { type T = [string, number] extends [string, unknown] ? 1 : 2 - const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Tuple([Type.String(), Type.Unknown()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Tuple([Type.String(), Type.Number()]), Type.Tuple([Type.String(), Type.Unknown()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Tuple 7', () => { type T = [] extends [string, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.Tuple([]), Type.Tuple([Type.String(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Tuple([]), Type.Tuple([Type.String(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Tuple 8', () => { type T = [string, number] extends [] ? 1 : 2 - const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Tuple([])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Tuple([Type.String(), Type.Number()]), Type.Tuple([])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Record 1', () => { type T = [string, number] extends Record ? 1 : 2 - const R = TypeExtends.Extends(Type.String(), Type.Record(Type.Number(), Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.String(), Type.Record(Type.Number(), Type.Any())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Record 2', () => { type T = [string, number] extends Record ? 1 : 2 - const R = TypeExtends.Extends(Type.String(), Type.Record(Type.Number(), Type.Unknown())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.String(), Type.Record(Type.Number(), Type.Unknown())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Record 3', () => { type T = [string, number] extends Record ? 1 : 2 - const R = TypeExtends.Extends(Type.String(), Type.Record(Type.Number(), Type.String())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.String(), Type.Record(Type.Number(), Type.String())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Record 4', () => { type T = [string, number] extends Record ? 1 : 2 - const R = TypeExtends.Extends(Type.String(), Type.Record(Type.Number(), Type.Number())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.String(), Type.Record(Type.Number(), Type.Number())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 1', () => { type T = [string, number] extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Object({}, { additionalProperties: false })) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Tuple([Type.String(), Type.Number()]), Type.Object({}, { additionalProperties: false })) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Object 2', () => { type T = [string, number] extends { a: 10 } ? 1 : 2 - const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Tuple([Type.String(), Type.Number()]), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 3', () => { type T = [string, number] extends object ? 1 : 2 - const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Object({})) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Tuple([Type.String(), Type.Number()]), Type.Object({})) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 1', () => { type T = [string, number] extends number | string ? 1 : 2 - const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Union([Type.Number(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Tuple([Type.String(), Type.Number()]), Type.Union([Type.Number(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 2', () => { type T = [string, number] extends any | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Union([Type.Any(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Tuple([Type.String(), Type.Number()]), Type.Union([Type.Any(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 2', () => { type T = [string, number] extends boolean | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Union([Type.Boolean(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Tuple([Type.String(), Type.Number()]), Type.Union([Type.Boolean(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Null', () => { type T = [string, number] extends null ? 1 : 2 - const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Null()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Tuple([Type.String(), Type.Number()]), Type.Null()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Undefined', () => { type T = [string, number] extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Undefined()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Tuple([Type.String(), Type.Number()]), Type.Undefined()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Void', () => { type T = [string, number] extends void ? 1 : 2 - const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Void()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Tuple([Type.String(), Type.Number()]), Type.Void()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Date', () => { type T = [string, number] extends Date ? 1 : 2 - const R = TypeExtends.Extends(Type.Tuple([Type.String(), Type.Number()]), Type.Date()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Tuple([Type.String(), Type.Number()]), Type.Date()) + Assert.IsEqual(R, ExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/uint8array.ts b/test/runtime/type/extends/uint8array.ts index 828163dd1..0128fb0cb 100644 --- a/test/runtime/type/extends/uint8array.ts +++ b/test/runtime/type/extends/uint8array.ts @@ -1,96 +1,95 @@ -import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import { Type, ExtendsCheck, ExtendsResult } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/extends/Uint8Array', () => { it('Should extend Any', () => { type T = Uint8Array extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.Uint8Array(), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Uint8Array(), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Unknown', () => { type T = Uint8Array extends unknown ? 1 : 2 - const R = TypeExtends.Extends(Type.Uint8Array(), Type.Unknown()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Uint8Array(), Type.Unknown()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend String', () => { type T = Uint8Array extends string ? 1 : 2 - const R = TypeExtends.Extends(Type.Uint8Array(), Type.String()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Uint8Array(), Type.String()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Boolean', () => { type T = Uint8Array extends boolean ? 1 : 2 - const R = TypeExtends.Extends(Type.Uint8Array(), Type.Boolean()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Uint8Array(), Type.Boolean()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Number', () => { type T = Uint8Array extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Uint8Array(), Type.Number()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Uint8Array(), Type.Number()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Integer', () => { type T = Uint8Array extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Uint8Array(), Type.Integer()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Uint8Array(), Type.Integer()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array', () => { type T = Uint8Array extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Uint8Array(), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Uint8Array(), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Tuple', () => { type T = Uint8Array extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.Uint8Array(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Uint8Array(), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Record', () => { type T = Uint8Array extends Record ? 1 : 2 - const R = TypeExtends.Extends(Type.Uint8Array(), Type.Record(Type.Number(), Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Uint8Array(), Type.Record(Type.Number(), Type.Any())) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Object 1', () => { type T = Uint8Array extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.Uint8Array(), Type.Object({}, { additionalProperties: false })) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Uint8Array(), Type.Object({}, { additionalProperties: false })) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Object 2', () => { type T = Uint8Array extends { a: 10 } ? 1 : 2 - const R = TypeExtends.Extends(Type.Uint8Array(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Uint8Array(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 1', () => { type T = Uint8Array extends number | string ? 1 : 2 - const R = TypeExtends.Extends(Type.Uint8Array(), Type.Union([Type.Number(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Uint8Array(), Type.Union([Type.Number(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 2', () => { type T = Uint8Array extends any | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Uint8Array(), Type.Union([Type.Any(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Uint8Array(), Type.Union([Type.Any(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 3', () => { type T = Uint8Array extends boolean | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Uint8Array(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Uint8Array(), Type.Union([Type.Boolean(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Null', () => { type T = Uint8Array extends null ? 1 : 2 - const R = TypeExtends.Extends(Type.Uint8Array(), Type.Null()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Uint8Array(), Type.Null()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Undefined', () => { type T = Uint8Array extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.Uint8Array(), Type.Undefined()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Uint8Array(), Type.Undefined()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Void', () => { type T = Uint8Array extends void ? 1 : 2 - const R = TypeExtends.Extends(Type.Uint8Array(), Type.Void()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Uint8Array(), Type.Void()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Date', () => { type T = Uint8Array extends Date ? 1 : 2 - const R = TypeExtends.Extends(Type.Uint8Array(), Type.Date()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Uint8Array(), Type.Date()) + Assert.IsEqual(R, ExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/undefined.ts b/test/runtime/type/extends/undefined.ts index 664126124..4996a1054 100644 --- a/test/runtime/type/extends/undefined.ts +++ b/test/runtime/type/extends/undefined.ts @@ -1,91 +1,90 @@ -import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import { Type, ExtendsCheck, ExtendsResult } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/extends/Undefined', () => { it('Should extend Any', () => { type T = undefined extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.Undefined(), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Undefined(), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend String', () => { type T = undefined extends string ? 1 : 2 - const R = TypeExtends.Extends(Type.Undefined(), Type.String()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Undefined(), Type.String()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Boolean', () => { type T = undefined extends boolean ? 1 : 2 - const R = TypeExtends.Extends(Type.Undefined(), Type.Boolean()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Undefined(), Type.Boolean()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Number', () => { type T = undefined extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Undefined(), Type.Number()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Undefined(), Type.Number()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Integer', () => { type T = undefined extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Undefined(), Type.Integer()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Undefined(), Type.Integer()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array', () => { type T = undefined extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Undefined(), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Undefined(), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Tuple', () => { type T = undefined extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.Undefined(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Undefined(), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 1', () => { type T = undefined extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.Undefined(), Type.Object({}, { additionalProperties: false })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Undefined(), Type.Object({}, { additionalProperties: false })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 2', () => { type T = undefined extends { a: 10 } ? 1 : 2 - const R = TypeExtends.Extends(Type.Undefined(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Undefined(), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 3', () => { type T = undefined extends object ? 1 : 2 - const R = TypeExtends.Extends(Type.Undefined(), Type.Object({})) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Undefined(), Type.Object({})) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 1', () => { type T = undefined extends number | string ? 1 : 2 - const R = TypeExtends.Extends(Type.Undefined(), Type.Union([Type.Number(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Undefined(), Type.Union([Type.Number(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 2', () => { type T = undefined extends any | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Undefined(), Type.Union([Type.Any(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Undefined(), Type.Union([Type.Any(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 2', () => { type T = undefined extends boolean | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Undefined(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Undefined(), Type.Union([Type.Boolean(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Null', () => { type T = undefined extends null ? 1 : 2 - const R = TypeExtends.Extends(Type.Undefined(), Type.Null()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Undefined(), Type.Null()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Undefined', () => { type T = undefined extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.Undefined(), Type.Undefined()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Undefined(), Type.Undefined()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Void', () => { type T = undefined extends void ? 1 : 2 - const R = TypeExtends.Extends(Type.Undefined(), Type.Void()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Undefined(), Type.Void()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Date', () => { type T = undefined extends Date ? 1 : 2 - const R = TypeExtends.Extends(Type.Undefined(), Type.Date()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Undefined(), Type.Date()) + Assert.IsEqual(R, ExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/union.ts b/test/runtime/type/extends/union.ts index e9eb05cef..39783cff9 100644 --- a/test/runtime/type/extends/union.ts +++ b/test/runtime/type/extends/union.ts @@ -1,136 +1,135 @@ -import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import { Type, ExtendsCheck, ExtendsResult } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/extends/Union', () => { it('Should extend Any', () => { type T = number | string extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Union([Type.Number(), Type.String()]), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend String', () => { type T = number | string extends string ? 1 : 2 - const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.String()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Union([Type.Number(), Type.String()]), Type.String()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Boolean', () => { type T = number | string extends boolean ? 1 : 2 - const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Boolean()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Union([Type.Number(), Type.String()]), Type.Boolean()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Number', () => { type T = number | string extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Number()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Union([Type.Number(), Type.String()]), Type.Number()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Integer', () => { type T = number | string extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Integer()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Union([Type.Number(), Type.String()]), Type.Integer()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array', () => { type T = number | string extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Union([Type.Number(), Type.String()]), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Tuple', () => { type T = number | string extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Union([Type.Number(), Type.String()]), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 1', () => { type T = number | string extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Object({}, { additionalProperties: false })) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Union([Type.Number(), Type.String()]), Type.Object({}, { additionalProperties: false })) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Object 2', () => { type T = number | string extends { a: 10 } ? 1 : 2 - const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Union([Type.Number(), Type.String()]), Type.Object({ a: Type.Literal(10) }, { additionalProperties: true })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 1', () => { type T = number | string extends number | string ? 1 : 2 - const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Union([Type.Number(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Union([Type.Number(), Type.String()]), Type.Union([Type.Number(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 2', () => { type T = number | string extends any | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Union([Type.Any(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Union([Type.Number(), Type.String()]), Type.Union([Type.Any(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 3', () => { type T = number | string extends boolean | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Union([Type.Boolean(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Union([Type.Number(), Type.String()]), Type.Union([Type.Boolean(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 4', () => { type T = any | boolean extends boolean | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Any(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.Union) + const R = ExtendsCheck(Type.Any(), Type.Union([Type.Boolean(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.Union) }) it('Should extend Union 5', () => { type T = any | string extends boolean | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Any(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.Union) + const R = ExtendsCheck(Type.Any(), Type.Union([Type.Boolean(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.Union) }) it('Should extend Union 6', () => { type T = any | {} extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.Any(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.Union) + const R = ExtendsCheck(Type.Any(), Type.Union([Type.Boolean(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.Union) }) it('Should extend Union 7', () => { type T = any extends boolean | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Any(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.Union) + const R = ExtendsCheck(Type.Any(), Type.Union([Type.Boolean(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.Union) }) it('Should extend Union 8', () => { type T = unknown | string extends boolean | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Union([Type.Unknown(), Type.String()]), Type.Union([Type.Boolean(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Union([Type.Unknown(), Type.String()]), Type.Union([Type.Boolean(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 9', () => { type T = unknown extends boolean | number ? 1 : 2 - const R = TypeExtends.Extends(Type.Unknown(), Type.Union([Type.Boolean(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Unknown(), Type.Union([Type.Boolean(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Null', () => { type T = number | string extends null ? 1 : 2 - const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Null()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Union([Type.Number(), Type.String()]), Type.Null()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Undefined', () => { type T = number | string extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Undefined()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Union([Type.Number(), Type.String()]), Type.Undefined()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Void', () => { type T = number | string extends void ? 1 : 2 - const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Void()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Union([Type.Number(), Type.String()]), Type.Void()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Void 2', () => { type T = number | string | void extends void ? 1 : 2 - const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Void()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Union([Type.Number(), Type.String()]), Type.Void()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Date', () => { type T = number | string | void extends Date ? 1 : 2 - const R = TypeExtends.Extends(Type.Union([Type.Number(), Type.String()]), Type.Date()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Union([Type.Number(), Type.String()]), Type.Date()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Date 2', () => { type T = Date | number | string | void extends Date ? 1 : 2 - const R = TypeExtends.Extends(Type.Union([Type.Date(), Type.Number(), Type.String()]), Type.Date()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Union([Type.Date(), Type.Number(), Type.String()]), Type.Date()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend BigInt', () => { type T = bigint | number | string | void extends Date ? 1 : 2 - const R = TypeExtends.Extends(Type.Union([Type.BigInt(), Type.Number(), Type.String()]), Type.Date()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Union([Type.BigInt(), Type.Number(), Type.String()]), Type.Date()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Symbol', () => { type T = symbol | number | string | void extends Date ? 1 : 2 - const R = TypeExtends.Extends(Type.Union([Type.Symbol(), Type.Number(), Type.String()]), Type.Date()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Union([Type.Symbol(), Type.Number(), Type.String()]), Type.Date()) + Assert.IsEqual(R, ExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/unknown.ts b/test/runtime/type/extends/unknown.ts index 85dbf8654..7622dbfdd 100644 --- a/test/runtime/type/extends/unknown.ts +++ b/test/runtime/type/extends/unknown.ts @@ -1,101 +1,100 @@ -import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import { Type, ExtendsCheck, ExtendsResult } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/extends/Unknown', () => { it('Should extend Any', () => { type T = unknown extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.Unknown(), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Unknown(), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Unknown', () => { type T = unknown extends unknown ? 1 : 2 - const R = TypeExtends.Extends(Type.Unknown(), Type.Unknown()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Unknown(), Type.Unknown()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend String', () => { type T = unknown extends string ? 1 : 2 - const R = TypeExtends.Extends(Type.Unknown(), Type.String()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Unknown(), Type.String()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Boolean', () => { type T = unknown extends boolean ? 1 : 2 - const R = TypeExtends.Extends(Type.Unknown(), Type.Boolean()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Unknown(), Type.Boolean()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Number', () => { type T = unknown extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Unknown(), Type.Number()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Unknown(), Type.Number()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Integer', () => { type T = unknown extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Unknown(), Type.Integer()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Unknown(), Type.Integer()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array 1', () => { type T = unknown extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Unknown(), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Unknown(), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array 2', () => { type T = unknown extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Unknown(), Type.Array(Type.String())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Unknown(), Type.Array(Type.String())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Tuple', () => { type T = unknown extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.Unknown(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Unknown(), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 1', () => { type T = unknown extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.Unknown(), Type.Object({})) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Unknown(), Type.Object({})) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 2', () => { type T = unknown extends { a: number } ? 1 : 2 - const R = TypeExtends.Extends(Type.Unknown(), Type.Object({ a: Type.Number() })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Unknown(), Type.Object({ a: Type.Number() })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 1', () => { type T = unknown extends number | string ? 1 : 2 - const R = TypeExtends.Extends(Type.Unknown(), Type.Union([Type.Number(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Unknown(), Type.Union([Type.Number(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 2', () => { type T = unknown extends any | number ? 1 : 2 // 1 - const R = TypeExtends.Extends(Type.Unknown(), Type.Union([Type.Any(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Unknown(), Type.Union([Type.Any(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 3', () => { type T = unknown extends unknown | number ? 1 : 2 // 1 - const R = TypeExtends.Extends(Type.Unknown(), Type.Union([Type.Unknown(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Unknown(), Type.Union([Type.Unknown(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 4', () => { type T = unknown extends unknown | any ? 1 : 2 // 1 - const R = TypeExtends.Extends(Type.Unknown(), Type.Union([Type.Unknown(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Unknown(), Type.Union([Type.Unknown(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Null', () => { type T = unknown extends null ? 1 : 2 - const R = TypeExtends.Extends(Type.Unknown(), Type.Null()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Unknown(), Type.Null()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Undefined', () => { type T = unknown extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.Unknown(), Type.Undefined()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Unknown(), Type.Undefined()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Void', () => { type T = unknown extends void ? 1 : 2 - const R = TypeExtends.Extends(Type.Unknown(), Type.Void()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Unknown(), Type.Void()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Date', () => { type T = unknown extends Date ? 1 : 2 - const R = TypeExtends.Extends(Type.Unknown(), Type.Date()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Unknown(), Type.Date()) + Assert.IsEqual(R, ExtendsResult.False) }) }) diff --git a/test/runtime/type/extends/void.ts b/test/runtime/type/extends/void.ts index 106e1759e..48efc644e 100644 --- a/test/runtime/type/extends/void.ts +++ b/test/runtime/type/extends/void.ts @@ -1,106 +1,105 @@ -import { TypeExtends, TypeExtendsResult } from '@sinclair/typebox' -import { Type } from '@sinclair/typebox' +import { Type, ExtendsCheck, ExtendsResult } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/extends/Void', () => { it('Should extend Any', () => { type T = void extends any ? 1 : 2 - const R = TypeExtends.Extends(Type.Void(), Type.Any()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Void(), Type.Any()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Unknown', () => { type T = void extends unknown ? 1 : 2 - const R = TypeExtends.Extends(Type.Void(), Type.Unknown()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Void(), Type.Unknown()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend String', () => { type T = void extends string ? 1 : 2 - const R = TypeExtends.Extends(Type.Void(), Type.String()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Void(), Type.String()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Boolean', () => { type T = void extends boolean ? 1 : 2 - const R = TypeExtends.Extends(Type.Void(), Type.Boolean()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Void(), Type.Boolean()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Number', () => { type T = void extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Void(), Type.Number()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Void(), Type.Number()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Integer', () => { type T = void extends number ? 1 : 2 - const R = TypeExtends.Extends(Type.Void(), Type.Integer()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Void(), Type.Integer()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array 1', () => { type T = void extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Void(), Type.Array(Type.Any())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Void(), Type.Array(Type.Any())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Array 2', () => { type T = void extends Array ? 1 : 2 - const R = TypeExtends.Extends(Type.Void(), Type.Array(Type.String())) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Void(), Type.Array(Type.String())) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Tuple', () => { type T = void extends [number, number] ? 1 : 2 - const R = TypeExtends.Extends(Type.Void(), Type.Tuple([Type.Number(), Type.Number()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Void(), Type.Tuple([Type.Number(), Type.Number()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 1', () => { type T = void extends object ? 1 : 2 - const R = TypeExtends.Extends(Type.Void(), Type.Object({})) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Void(), Type.Object({})) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 2', () => { type T = void extends {} ? 1 : 2 - const R = TypeExtends.Extends(Type.Void(), Type.Object({})) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Void(), Type.Object({})) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Object 3', () => { type T = void extends { a: number } ? 1 : 2 - const R = TypeExtends.Extends(Type.Void(), Type.Object({ a: Type.Number() })) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Void(), Type.Object({ a: Type.Number() })) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 1', () => { type T = void extends number | string ? 1 : 2 - const R = TypeExtends.Extends(Type.Void(), Type.Union([Type.Number(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Void(), Type.Union([Type.Number(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Union 2', () => { type T = void extends any | number ? 1 : 2 // 1 - const R = TypeExtends.Extends(Type.Void(), Type.Union([Type.Any(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Void(), Type.Union([Type.Any(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 3', () => { type T = void extends unknown | number ? 1 : 2 // 1 - const R = TypeExtends.Extends(Type.Void(), Type.Union([Type.Unknown(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Void(), Type.Union([Type.Unknown(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Union 4', () => { type T = void extends unknown | any ? 1 : 2 // 1 - const R = TypeExtends.Extends(Type.Void(), Type.Union([Type.Unknown(), Type.String()])) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Void(), Type.Union([Type.Unknown(), Type.String()])) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Null', () => { type T = void extends null ? 1 : 2 - const R = TypeExtends.Extends(Type.Void(), Type.Null()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Void(), Type.Null()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Undefined', () => { type T = void extends undefined ? 1 : 2 - const R = TypeExtends.Extends(Type.Void(), Type.Undefined()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Void(), Type.Undefined()) + Assert.IsEqual(R, ExtendsResult.False) }) it('Should extend Void', () => { type T = void extends void ? 1 : 2 - const R = TypeExtends.Extends(Type.Void(), Type.Void()) - Assert.IsEqual(R, TypeExtendsResult.True) + const R = ExtendsCheck(Type.Void(), Type.Void()) + Assert.IsEqual(R, ExtendsResult.True) }) it('Should extend Date', () => { type T = void extends Date ? 1 : 2 - const R = TypeExtends.Extends(Type.Void(), Type.Date()) - Assert.IsEqual(R, TypeExtendsResult.False) + const R = ExtendsCheck(Type.Void(), Type.Date()) + Assert.IsEqual(R, ExtendsResult.False) }) }) diff --git a/test/runtime/type/guard/const.ts b/test/runtime/type/guard/const.ts new file mode 100644 index 000000000..bc137c569 --- /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.TReadonly(T)) + Assert.IsTrue(TypeGuard.TUndefined(T)) + }) + it('Should guard for TConst 2', () => { + const T = Type.Const(null) + Assert.IsFalse(TypeGuard.TReadonly(T)) + Assert.IsTrue(TypeGuard.TNull(T)) + }) + it('Should guard for TConst 3', () => { + const T = Type.Const(Symbol()) + Assert.IsFalse(TypeGuard.TReadonly(T)) + Assert.IsTrue(TypeGuard.TSymbol(T)) + }) + it('Should guard for TConst 4', () => { + const T = Type.Const(1 as const) + Assert.IsFalse(TypeGuard.TReadonly(T)) + Assert.IsTrue(TypeGuard.TLiteral(T)) + Assert.IsEqual(T.const, 1) + }) + it('Should guard for TConst 5', () => { + const T = Type.Const('hello' as const) + Assert.IsFalse(TypeGuard.TReadonly(T)) + Assert.IsTrue(TypeGuard.TLiteral(T)) + Assert.IsEqual(T.const, 'hello') + }) + it('Should guard for TConst 6', () => { + const T = Type.Const(true as const) + Assert.IsFalse(TypeGuard.TReadonly(T)) + Assert.IsTrue(TypeGuard.TLiteral(T)) + Assert.IsEqual(T.const, true) + }) + // ---------------------------------------------------------------- + // Complex Types + // ---------------------------------------------------------------- + it('Should guard for TConst 7', () => { + const T = Type.Const(100n as const) + Assert.IsFalse(TypeGuard.TReadonly(T)) + // TS disparity because TLiteral does not support Bigint + Assert.IsTrue(TypeGuard.TBigInt(T)) + }) + it('Should guard for TConst 8', () => { + const T = Type.Const(new Date()) + Assert.IsFalse(TypeGuard.TReadonly(T)) + Assert.IsTrue(TypeGuard.TDate(T)) + }) + it('Should guard for TConst 9', () => { + const T = Type.Const(new Uint8Array()) + Assert.IsFalse(TypeGuard.TReadonly(T)) + Assert.IsTrue(TypeGuard.TUint8Array(T)) + }) + it('Should guard for TConst 10', () => { + const T = Type.Const(function () {}) + Assert.IsFalse(TypeGuard.TReadonly(T)) + Assert.IsTrue(TypeGuard.TFunction(T)) + Assert.IsTrue(T.parameters.length === 0) + Assert.IsTrue(TypeGuard.TUnknown(T.returns)) + }) + it('Should guard for TConst 11', () => { + const T = Type.Const(new (class {})()) + Assert.IsFalse(TypeGuard.TReadonly(T)) + Assert.IsTrue(TypeGuard.TObject(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.TReadonly(T)) + Assert.IsTrue(TypeGuard.TAny(T)) + }) + it('Should guard for TConst 13', () => { + const T = Type.Const((async function* (): any {})()) + const R = ValueGuard.IsAsyncIterator((function* (): any {})()) + Assert.IsFalse(TypeGuard.TReadonly(T)) + Assert.IsTrue(TypeGuard.TAny(T)) + }) + it('Should guard for TConst 14', () => { + const T = Type.Const({ + x: 1, + y: { + z: 2, + }, + } as const) + // root + Assert.IsFalse(TypeGuard.TReadonly(T)) + Assert.IsTrue(TypeGuard.TObject(T)) + // x + Assert.IsTrue(TypeGuard.TLiteral(T.properties.x)) + Assert.IsEqual(T.properties.x.const, 1) + // y + Assert.IsTrue(TypeGuard.TReadonly(T.properties.y)) + Assert.IsTrue(TypeGuard.TObject(T.properties.y)) + // y.z + Assert.IsTrue(TypeGuard.TReadonly(T.properties.y.properties.z)) + Assert.IsTrue(TypeGuard.TLiteral(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.TReadonly(T)) + Assert.IsTrue(TypeGuard.TTuple(T)) + Assert.IsTrue(T.items?.length === 3) + // 0 + Assert.IsFalse(TypeGuard.TReadonly(T.items![0])) + Assert.IsTrue(TypeGuard.TLiteral(T.items![0])) + // 1 + Assert.IsFalse(TypeGuard.TReadonly(T.items![1])) + Assert.IsTrue(TypeGuard.TLiteral(T.items![1])) + // 2 + Assert.IsFalse(TypeGuard.TReadonly(T.items![2])) + Assert.IsTrue(TypeGuard.TLiteral(T.items![2])) + }) +}) diff --git a/test/runtime/type/guard/deref.ts b/test/runtime/type/guard/deref.ts new file mode 100644 index 000000000..d37047279 --- /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.TString(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.TObject(D)) + Assert.IsTrue(TypeGuard.TString(D.properties.x)) + Assert.IsTrue(TypeGuard.TString(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.TArray(D)) + Assert.IsTrue(TypeGuard.TString(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.TAsyncIterator(D)) + Assert.IsTrue(TypeGuard.TString(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.TIterator(D)) + Assert.IsTrue(TypeGuard.TString(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.TFunction(D)) + Assert.IsTrue(TypeGuard.TString(D.parameters[0])) + Assert.IsTrue(TypeGuard.TString(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.TConstructor(D)) + Assert.IsTrue(TypeGuard.TString(D.parameters[0])) + Assert.IsTrue(TypeGuard.TString(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.TPromise(D)) + Assert.IsTrue(TypeGuard.TString(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.TArray(D)) + Assert.IsTrue(TypeGuard.TString(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/index.ts b/test/runtime/type/guard/index.ts index 3533d6d53..bd6efde87 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' diff --git a/test/runtime/type/guard/indexed.ts b/test/runtime/type/guard/indexed.ts index 2a680f843..fa7bb47b6 100644 --- a/test/runtime/type/guard/indexed.ts +++ b/test/runtime/type/guard/indexed.ts @@ -311,8 +311,17 @@ describe('type/guard/TIndex', () => { }) it('Should Index 34', () => { const T = Type.String() - // @ts-ignore const I = Type.Index(T, ['x']) Assert.IsTrue(TypeGuard.TNever(I)) }) + it('Should Index 35', () => { + const T = Type.Array(Type.String()) + const I = Type.Index(T, Type.Number()) + Assert.IsTrue(TypeGuard.TString(I)) + }) + it('Should Index 36', () => { + const T = Type.Array(Type.String()) + const I = Type.Index(T, ['[number]']) + Assert.IsTrue(TypeGuard.TString(I)) + }) }) diff --git a/test/runtime/type/guard/omit.ts b/test/runtime/type/guard/omit.ts index 7b19d146c..a68749ab1 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', () => { @@ -124,6 +124,6 @@ describe('type/guard/TOmit', () => { .Decode((value) => value) .Encode((value) => value) const R = Type.Omit(S, ['x']) - Assert.IsFalse(Transform in R) + Assert.IsFalse(TransformKind in R) }) }) diff --git a/test/runtime/type/guard/partial.ts b/test/runtime/type/guard/partial.ts index cd248bb31..ae7c3dd69 100644 --- a/test/runtime/type/guard/partial.ts +++ b/test/runtime/type/guard/partial.ts @@ -1,4 +1,4 @@ -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', () => { @@ -62,6 +62,6 @@ describe('type/guard/TPartial', () => { .Decode((value) => value) .Encode((value) => value) const R = Type.Partial(S) - Assert.IsFalse(Transform in R) + Assert.IsFalse(TransformKind in R) }) }) diff --git a/test/runtime/type/guard/pick.ts b/test/runtime/type/guard/pick.ts index 0fcd90573..d151f14c3 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', () => { @@ -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/required.ts b/test/runtime/type/guard/required.ts index 5af8134d5..9e615cc5b 100644 --- a/test/runtime/type/guard/required.ts +++ b/test/runtime/type/guard/required.ts @@ -1,4 +1,4 @@ -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', () => { @@ -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/union.ts b/test/runtime/type/guard/union.ts index 62b485e40..0a52c57c6 100644 --- a/test/runtime/type/guard/union.ts +++ b/test/runtime/type/guard/union.ts @@ -1,4 +1,4 @@ -import { TypeGuard } from '@sinclair/typebox' +import { TSchema, TypeGuard } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' import { Assert } from '../../assert/index' @@ -46,7 +46,7 @@ describe('type/guard/TUnion', () => { Type.Object({ x: Type.Number(), }), - {} as any, + {} as TSchema, ]), ) Assert.IsFalse(R) diff --git a/test/runtime/type/intrinsic/intrinsic.ts b/test/runtime/type/intrinsic/intrinsic.ts index 3ff191aee..7f9efd318 100644 --- a/test/runtime/type/intrinsic/intrinsic.ts +++ b/test/runtime/type/intrinsic/intrinsic.ts @@ -1,21 +1,22 @@ -import { TypeGuard, Intrinsic } from '@sinclair/typebox' +import { TypeGuard } from '@sinclair/typebox' +import { IntrinsicResolve } from '@sinclair/typebox' import { Type } from '@sinclair/typebox' import { Assert } from '../../assert/index' -describe('type/intrinsic/Map', () => { +describe('type/intrinsic/IntrinsicResolve', () => { // ---------------------------------------------------- // Passthrough // ---------------------------------------------------- it('Should passthrough 1', () => { - const T = Intrinsic.Map(Type.String(), 'Capitalize') + const T = IntrinsicResolve(Type.String(), 'Capitalize') Assert.IsTrue(TypeGuard.TString(T)) }) it('Should passthrough 2', () => { - const T = Intrinsic.Map(Type.Number(), 'Capitalize') + const T = IntrinsicResolve(Type.Number(), 'Capitalize') Assert.IsTrue(TypeGuard.TNumber(T)) }) it('Should passthrough 3', () => { - const T = Intrinsic.Map( + const T = IntrinsicResolve( Type.Object({ x: Type.Number(), }), @@ -30,7 +31,7 @@ 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') + const T = IntrinsicResolve(U, 'Capitalize') Assert.IsTrue(TypeGuard.TUnion(T)) Assert.IsTrue(TypeGuard.TObject(T.anyOf[0])) Assert.IsTrue(TypeGuard.TLiteral(T.anyOf[1])) @@ -40,22 +41,22 @@ describe('type/intrinsic/Map', () => { // Mode: Literal // ---------------------------------------------------- it('Should map literal: Capitalize', () => { - const T = Intrinsic.Map(Type.Literal('hello'), 'Capitalize') + const T = IntrinsicResolve(Type.Literal('hello'), 'Capitalize') Assert.IsTrue(TypeGuard.TLiteral(T)) Assert.IsEqual(T.const, 'Hello') }) it('Should map literal: Uncapitalize', () => { - const T = Intrinsic.Map(Type.Literal('HELLO'), 'Uncapitalize') + const T = IntrinsicResolve(Type.Literal('HELLO'), 'Uncapitalize') Assert.IsTrue(TypeGuard.TLiteral(T)) Assert.IsEqual(T.const, 'hELLO') }) it('Should map literal: Uppercase', () => { - const T = Intrinsic.Map(Type.Literal('hello'), 'Uppercase') + const T = IntrinsicResolve(Type.Literal('hello'), 'Uppercase') Assert.IsTrue(TypeGuard.TLiteral(T)) Assert.IsEqual(T.const, 'HELLO') }) it('Should map literal: Lowercase', () => { - const T = Intrinsic.Map(Type.Literal('HELLO'), 'Lowercase') + const T = IntrinsicResolve(Type.Literal('HELLO'), 'Lowercase') Assert.IsTrue(TypeGuard.TLiteral(T)) Assert.IsEqual(T.const, 'hello') }) @@ -63,25 +64,25 @@ describe('type/intrinsic/Map', () => { // Mode: Literal Union // ---------------------------------------------------- it('Should map literal union: Capitalize', () => { - const T = Intrinsic.Map(Type.Union([Type.Literal('hello'), Type.Literal('world')]), 'Capitalize') + const T = IntrinsicResolve(Type.Union([Type.Literal('hello'), Type.Literal('world')]), 'Capitalize') Assert.IsTrue(TypeGuard.TUnion(T)) Assert.IsEqual(T.anyOf[0].const, 'Hello') Assert.IsEqual(T.anyOf[1].const, 'World') }) it('Should map literal union: Uncapitalize', () => { - const T = Intrinsic.Map(Type.Union([Type.Literal('Hello'), Type.Literal('World')]), 'Uncapitalize') + const T = IntrinsicResolve(Type.Union([Type.Literal('Hello'), Type.Literal('World')]), 'Uncapitalize') Assert.IsTrue(TypeGuard.TUnion(T)) Assert.IsEqual(T.anyOf[0].const, 'hello') Assert.IsEqual(T.anyOf[1].const, 'world') }) it('Should map literal union: Uppercase', () => { - const T = Intrinsic.Map(Type.Union([Type.Literal('hello'), Type.Literal('world')]), 'Uppercase') + const T = IntrinsicResolve(Type.Union([Type.Literal('hello'), Type.Literal('world')]), 'Uppercase') Assert.IsTrue(TypeGuard.TUnion(T)) Assert.IsEqual(T.anyOf[0].const, 'HELLO') Assert.IsEqual(T.anyOf[1].const, 'WORLD') }) it('Should map literal union: Lowercase', () => { - const T = Intrinsic.Map(Type.Union([Type.Literal('HELLO'), Type.Literal('WORLD')]), 'Lowercase') + const T = IntrinsicResolve(Type.Union([Type.Literal('HELLO'), Type.Literal('WORLD')]), 'Lowercase') Assert.IsTrue(TypeGuard.TUnion(T)) Assert.IsEqual(T.anyOf[0].const, 'hello') Assert.IsEqual(T.anyOf[1].const, 'world') @@ -90,22 +91,22 @@ describe('type/intrinsic/Map', () => { // Mode: TemplateLiteral // ---------------------------------------------------- it('Should map template literal: Capitalize', () => { - const T = Intrinsic.Map(Type.TemplateLiteral('hello${1|2}world'), 'Capitalize') + const T = IntrinsicResolve(Type.TemplateLiteral('hello${1|2}world'), 'Capitalize') Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) Assert.IsEqual(T.pattern, '^(Hello1world|Hello2world)$') }) it('Should map template literal: Uncapitalize', () => { - const T = Intrinsic.Map(Type.TemplateLiteral('HELLO${1|2}WORLD'), 'Uncapitalize') + const T = IntrinsicResolve(Type.TemplateLiteral('HELLO${1|2}WORLD'), 'Uncapitalize') Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) Assert.IsEqual(T.pattern, '^(hELLO1WORLD|hELLO2WORLD)$') }) it('Should map template literal: Uppercase', () => { - const T = Intrinsic.Map(Type.TemplateLiteral('hello${1|2}world'), 'Uppercase') + const T = IntrinsicResolve(Type.TemplateLiteral('hello${1|2}world'), 'Uppercase') Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) Assert.IsEqual(T.pattern, '^(HELLO1WORLD|HELLO2WORLD)$') }) it('Should map template literal: Lowercase', () => { - const T = Intrinsic.Map(Type.TemplateLiteral('HELLO${1|2}WORLD'), 'Lowercase') + const T = IntrinsicResolve(Type.TemplateLiteral('HELLO${1|2}WORLD'), 'Lowercase') Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) Assert.IsEqual(T.pattern, '^(hello1world|hello2world)$') }) @@ -113,22 +114,22 @@ describe('type/intrinsic/Map', () => { // 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') + const T = IntrinsicResolve(Type.TemplateLiteral([Type.Literal('hello'), Type.Union([Type.Literal(1), Type.Literal(2)])]), 'Capitalize') Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) Assert.IsEqual(T.pattern, '^(Hello1|Hello2)$') }) it('Should map template literal numeric: Uncapitalize', () => { - const T = Intrinsic.Map(Type.TemplateLiteral([Type.Literal('HELLO'), Type.Union([Type.Literal(1), Type.Literal(2)])]), 'Uncapitalize') + const T = IntrinsicResolve(Type.TemplateLiteral([Type.Literal('HELLO'), Type.Union([Type.Literal(1), Type.Literal(2)])]), 'Uncapitalize') Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) Assert.IsEqual(T.pattern, '^(hELLO1|hELLO2)$') }) it('Should map template literal numeric: Uppercase', () => { - const T = Intrinsic.Map(Type.TemplateLiteral([Type.Literal('hello'), Type.Union([Type.Literal(1), Type.Literal(2)])]), 'Uppercase') + const T = IntrinsicResolve(Type.TemplateLiteral([Type.Literal('hello'), Type.Union([Type.Literal(1), Type.Literal(2)])]), 'Uppercase') Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) Assert.IsEqual(T.pattern, '^(HELLO1|HELLO2)$') }) it('Should map template literal numeric: Lowercase', () => { - const T = Intrinsic.Map(Type.TemplateLiteral([Type.Literal('HELLO'), Type.Union([Type.Literal(1), Type.Literal(2)])]), 'Lowercase') + const T = IntrinsicResolve(Type.TemplateLiteral([Type.Literal('HELLO'), Type.Union([Type.Literal(1), Type.Literal(2)])]), 'Lowercase') Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) Assert.IsEqual(T.pattern, '^(hello1|hello2)$') }) @@ -136,27 +137,27 @@ describe('type/intrinsic/Map', () => { // Mode: TemplateLiteral Patterns // ---------------------------------------------------- it('Should map template literal patterns 1', () => { - const T = Intrinsic.Map(Type.TemplateLiteral('HELLO${string}WORLD'), 'Lowercase') + const T = IntrinsicResolve(Type.TemplateLiteral('HELLO${string}WORLD'), 'Lowercase') Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) Assert.IsEqual(T.pattern, '^hello(.*)world$') }) it('Should map template literal patterns 2', () => { - const T = Intrinsic.Map(Type.TemplateLiteral('HELLO${number}WORLD'), 'Lowercase') + const T = IntrinsicResolve(Type.TemplateLiteral('HELLO${number}WORLD'), 'Lowercase') Assert.IsTrue(TypeGuard.TTemplateLiteral(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') + const T = IntrinsicResolve(Type.TemplateLiteral('${number}${string}'), 'Lowercase') Assert.IsTrue(TypeGuard.TTemplateLiteral(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') + const T = IntrinsicResolve(Type.TemplateLiteral('${number}HELLO${string}'), 'Lowercase') Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) Assert.IsEqual(T.pattern, '^(0|[1-9][0-9]*)hello(.*)$') }) it('Should map template literal patterns 3', () => { - const T = Intrinsic.Map(Type.TemplateLiteral('${string|number}HELLO'), 'Lowercase') + const T = IntrinsicResolve(Type.TemplateLiteral('${string|number}HELLO'), 'Lowercase') Assert.IsTrue(TypeGuard.TTemplateLiteral(T)) Assert.IsEqual(T.pattern, '^(stringhello|numberhello)$') }) diff --git a/test/runtime/type/template/finite.ts b/test/runtime/type/template/finite.ts index 53028dff9..a59d5c098 100644 --- a/test/runtime/type/template/finite.ts +++ b/test/runtime/type/template/finite.ts @@ -1,71 +1,71 @@ -import { PatternString, PatternBoolean, PatternNumber, TemplateLiteralParser, TemplateLiteralFinite } from '@sinclair/typebox' +import { TemplateLiteralParse, IsTemplateLiteralFinite, PatternString, PatternBoolean, PatternNumber } from '@sinclair/typebox' import { Assert } from '../../assert/index' -describe('type/templateliteral/TemplateLiteralFinite', () => { +describe('type/templateliteral/IsTemplateLiteralFinite', () => { // --------------------------------------------------------------- // Finite // --------------------------------------------------------------- it('Finite 1', () => { - const E = TemplateLiteralParser.Parse(`A`) - const R = TemplateLiteralFinite.Check(E) + const E = TemplateLiteralParse(`A`) + const R = IsTemplateLiteralFinite(E) Assert.IsTrue(R) }) it('Finite 2', () => { - const E = TemplateLiteralParser.Parse(`A|B`) - const R = TemplateLiteralFinite.Check(E) + const E = TemplateLiteralParse(`A|B`) + const R = IsTemplateLiteralFinite(E) Assert.IsTrue(R) }) it('Finite 3', () => { - const E = TemplateLiteralParser.Parse(`A(B|C)`) - const R = TemplateLiteralFinite.Check(E) + const E = TemplateLiteralParse(`A(B|C)`) + const R = IsTemplateLiteralFinite(E) Assert.IsTrue(R) }) it('Finite 4', () => { - const E = TemplateLiteralParser.Parse(`${PatternBoolean}`) - const R = TemplateLiteralFinite.Check(E) + const E = TemplateLiteralParse(`${PatternBoolean}`) + const R = IsTemplateLiteralFinite(E) Assert.IsTrue(R) }) it('Finite 5', () => { - const E = TemplateLiteralParser.Parse(`\\.\\*`) - const R = TemplateLiteralFinite.Check(E) + const E = TemplateLiteralParse(`\\.\\*`) + const R = IsTemplateLiteralFinite(E) Assert.IsTrue(R) }) // --------------------------------------------------------------- // Infinite // --------------------------------------------------------------- it('Infinite 1', () => { - const E = TemplateLiteralParser.Parse(`${PatternString}`) - const R = TemplateLiteralFinite.Check(E) + const E = TemplateLiteralParse(`${PatternString}`) + const R = IsTemplateLiteralFinite(E) Assert.IsFalse(R) }) it('Infinite 2', () => { - const E = TemplateLiteralParser.Parse(`${PatternNumber}`) - const R = TemplateLiteralFinite.Check(E) + const E = TemplateLiteralParse(`${PatternNumber}`) + const R = IsTemplateLiteralFinite(E) Assert.IsFalse(R) }) it('Infinite 3', () => { - const E = TemplateLiteralParser.Parse(`A|${PatternString}`) - const R = TemplateLiteralFinite.Check(E) + const E = TemplateLiteralParse(`A|${PatternString}`) + const R = IsTemplateLiteralFinite(E) Assert.IsFalse(R) }) it('Infinite 4', () => { - const E = TemplateLiteralParser.Parse(`A|${PatternNumber}`) - const R = TemplateLiteralFinite.Check(E) + const E = TemplateLiteralParse(`A|${PatternNumber}`) + const R = IsTemplateLiteralFinite(E) Assert.IsFalse(R) }) it('Infinite 5', () => { - const E = TemplateLiteralParser.Parse(`A(${PatternString})`) - const R = TemplateLiteralFinite.Check(E) + const E = TemplateLiteralParse(`A(${PatternString})`) + const R = IsTemplateLiteralFinite(E) Assert.IsFalse(R) }) it('Infinite 6', () => { - const E = TemplateLiteralParser.Parse(`A(${PatternNumber})`) - const R = TemplateLiteralFinite.Check(E) + const E = TemplateLiteralParse(`A(${PatternNumber})`) + const R = IsTemplateLiteralFinite(E) Assert.IsFalse(R) }) it('Infinite 7', () => { - const E = TemplateLiteralParser.Parse(`${PatternString}_foo`) - const R = TemplateLiteralFinite.Check(E) + const E = TemplateLiteralParse(`${PatternString}_foo`) + const R = IsTemplateLiteralFinite(E) Assert.IsFalse(R) }) }) diff --git a/test/runtime/type/template/generate.ts b/test/runtime/type/template/generate.ts index 73aa9cfc3..8519ca25b 100644 --- a/test/runtime/type/template/generate.ts +++ b/test/runtime/type/template/generate.ts @@ -1,4 +1,4 @@ -import { TemplateLiteralParser, TemplateLiteralGenerator } from '@sinclair/typebox' +import { TemplateLiteralParse, TemplateLiteralGenerate } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/templateliteral/TemplateLiteralGenerator', () => { @@ -6,194 +6,194 @@ describe('type/templateliteral/TemplateLiteralGenerator', () => { // Exact (No Default Unwrap) // --------------------------------------------------------------- it('Exact 1', () => { - const E = TemplateLiteralParser.Parse('^$') - const R = [...TemplateLiteralGenerator.Generate(E)] + const E = TemplateLiteralParse('^$') + const R = [...TemplateLiteralGenerate(E)] Assert.IsEqual(R, ['^$']) }) it('Exact 2', () => { - const E = TemplateLiteralParser.Parse('^A$') - const R = [...TemplateLiteralGenerator.Generate(E)] + const E = TemplateLiteralParse('^A$') + const R = [...TemplateLiteralGenerate(E)] Assert.IsEqual(R, ['^A$']) }) // --------------------------------------------------------------- // Patterns // --------------------------------------------------------------- it('Pattern 1', () => { - const E = TemplateLiteralParser.Parse('(true|false)') - const R = [...TemplateLiteralGenerator.Generate(E)] + const E = TemplateLiteralParse('(true|false)') + const R = [...TemplateLiteralGenerate(E)] Assert.IsEqual(R, ['true', 'false']) }) it('Pattern 2', () => { - const E = TemplateLiteralParser.Parse('(0|[1-9][0-9]*)') - const R = [...TemplateLiteralGenerator.Generate(E)] + const E = TemplateLiteralParse('(0|[1-9][0-9]*)') + const R = [...TemplateLiteralGenerate(E)] Assert.IsEqual(R, ['0', '[1-9][0-9]*']) }) it('Pattern 3', () => { - const E = TemplateLiteralParser.Parse('.*') - const R = [...TemplateLiteralGenerator.Generate(E)] + const E = TemplateLiteralParse('.*') + const R = [...TemplateLiteralGenerate(E)] Assert.IsEqual(R, ['.*']) }) // --------------------------------------------------------------- // Expression // --------------------------------------------------------------- it('Expression 1', () => { - const E = TemplateLiteralParser.Parse(')') - const R = [...TemplateLiteralGenerator.Generate(E)] + const E = TemplateLiteralParse(')') + const R = [...TemplateLiteralGenerate(E)] Assert.IsEqual(R, [')']) }) it('Expression 2', () => { - const E = TemplateLiteralParser.Parse('\\)') - const R = [...TemplateLiteralGenerator.Generate(E)] + const E = TemplateLiteralParse('\\)') + const R = [...TemplateLiteralGenerate(E)] Assert.IsEqual(R, ['\\)']) }) it('Expression 3', () => { - const E = TemplateLiteralParser.Parse('\\(') - const R = [...TemplateLiteralGenerator.Generate(E)] + const E = TemplateLiteralParse('\\(') + const R = [...TemplateLiteralGenerate(E)] Assert.IsEqual(R, ['\\(']) }) it('Expression 4', () => { - const E = TemplateLiteralParser.Parse('') - const R = [...TemplateLiteralGenerator.Generate(E)] + const E = TemplateLiteralParse('') + const R = [...TemplateLiteralGenerate(E)] Assert.IsEqual(R, ['']) }) it('Expression 5', () => { - const E = TemplateLiteralParser.Parse('\\') - const R = [...TemplateLiteralGenerator.Generate(E)] + const E = TemplateLiteralParse('\\') + const R = [...TemplateLiteralGenerate(E)] Assert.IsEqual(R, ['\\']) }) it('Expression 6', () => { - const E = TemplateLiteralParser.Parse('()') - const R = [...TemplateLiteralGenerator.Generate(E)] + const E = TemplateLiteralParse('()') + const R = [...TemplateLiteralGenerate(E)] Assert.IsEqual(R, ['']) }) it('Expression 7', () => { - const E = TemplateLiteralParser.Parse('(a)') - const R = [...TemplateLiteralGenerator.Generate(E)] + const E = TemplateLiteralParse('(a)') + const R = [...TemplateLiteralGenerate(E)] Assert.IsEqual(R, ['a']) }) it('Expression 8', () => { - const E = TemplateLiteralParser.Parse('()))') - const R = [...TemplateLiteralGenerator.Generate(E)] + const E = TemplateLiteralParse('()))') + const R = [...TemplateLiteralGenerate(E)] Assert.IsEqual(R, ['))']) }) it('Expression 9', () => { - const E = TemplateLiteralParser.Parse('())') - const R = [...TemplateLiteralGenerator.Generate(E)] + const E = TemplateLiteralParse('())') + const R = [...TemplateLiteralGenerate(E)] Assert.IsEqual(R, [')']) }) it('Expression 10', () => { - const E = TemplateLiteralParser.Parse('A|B') - const R = [...TemplateLiteralGenerator.Generate(E)] + const E = TemplateLiteralParse('A|B') + const R = [...TemplateLiteralGenerate(E)] Assert.IsEqual(R, ['A', 'B']) }) it('Expression 11', () => { - const E = TemplateLiteralParser.Parse('A|(B)') - const R = [...TemplateLiteralGenerator.Generate(E)] + const E = TemplateLiteralParse('A|(B)') + const R = [...TemplateLiteralGenerate(E)] Assert.IsEqual(R, ['A', 'B']) }) it('Expression 12', () => { - const E = TemplateLiteralParser.Parse('A(B)') - const R = [...TemplateLiteralGenerator.Generate(E)] + const E = TemplateLiteralParse('A(B)') + const R = [...TemplateLiteralGenerate(E)] Assert.IsEqual(R, ['AB']) }) it('Expression 13', () => { - const E = TemplateLiteralParser.Parse('(A)B') - const R = [...TemplateLiteralGenerator.Generate(E)] + const E = TemplateLiteralParse('(A)B') + const R = [...TemplateLiteralGenerate(E)] Assert.IsEqual(R, ['AB']) }) it('Expression 14', () => { - const E = TemplateLiteralParser.Parse('(A)|B') - const R = [...TemplateLiteralGenerator.Generate(E)] + const E = TemplateLiteralParse('(A)|B') + const R = [...TemplateLiteralGenerate(E)] Assert.IsEqual(R, ['A', 'B']) }) it('Expression 15', () => { - const E = TemplateLiteralParser.Parse('|') - const R = [...TemplateLiteralGenerator.Generate(E)] + const E = TemplateLiteralParse('|') + const R = [...TemplateLiteralGenerate(E)] Assert.IsEqual(R, ['']) }) it('Expression 16', () => { - const E = TemplateLiteralParser.Parse('||') - const R = [...TemplateLiteralGenerator.Generate(E)] + const E = TemplateLiteralParse('||') + const R = [...TemplateLiteralGenerate(E)] Assert.IsEqual(R, ['']) }) it('Expression 17', () => { - const E = TemplateLiteralParser.Parse('||A') - const R = [...TemplateLiteralGenerator.Generate(E)] + const E = TemplateLiteralParse('||A') + const R = [...TemplateLiteralGenerate(E)] Assert.IsEqual(R, ['A']) }) it('Expression 18', () => { - const E = TemplateLiteralParser.Parse('A||') - const R = [...TemplateLiteralGenerator.Generate(E)] + const E = TemplateLiteralParse('A||') + const R = [...TemplateLiteralGenerate(E)] Assert.IsEqual(R, ['A']) }) it('Expression 19', () => { - const E = TemplateLiteralParser.Parse('A||B') - const R = [...TemplateLiteralGenerator.Generate(E)] + const E = TemplateLiteralParse('A||B') + const R = [...TemplateLiteralGenerate(E)] Assert.IsEqual(R, ['A', 'B']) }) it('Expression 20', () => { - const E = TemplateLiteralParser.Parse('A|()|B') - const R = [...TemplateLiteralGenerator.Generate(E)] + const E = TemplateLiteralParse('A|()|B') + const R = [...TemplateLiteralGenerate(E)] Assert.IsEqual(R, ['A', '', 'B']) }) it('Expression 21', () => { - const E = TemplateLiteralParser.Parse('A|(|)|B') - const R = [...TemplateLiteralGenerator.Generate(E)] + const E = TemplateLiteralParse('A|(|)|B') + const R = [...TemplateLiteralGenerate(E)] Assert.IsEqual(R, ['A', '', 'B']) }) it('Expression 22', () => { - const E = TemplateLiteralParser.Parse('A|(||)|B') - const R = [...TemplateLiteralGenerator.Generate(E)] + const E = TemplateLiteralParse('A|(||)|B') + const R = [...TemplateLiteralGenerate(E)] Assert.IsEqual(R, ['A', '', 'B']) }) it('Expression 23', () => { - const E = TemplateLiteralParser.Parse('|A(||)B|') - const R = [...TemplateLiteralGenerator.Generate(E)] + const E = TemplateLiteralParse('|A(||)B|') + const R = [...TemplateLiteralGenerate(E)] Assert.IsEqual(R, ['AB']) }) it('Expression 24', () => { - const E = TemplateLiteralParser.Parse('A(B)(C)') - const R = [...TemplateLiteralGenerator.Generate(E)] + const E = TemplateLiteralParse('A(B)(C)') + const R = [...TemplateLiteralGenerate(E)] Assert.IsEqual(R, ['ABC']) }) it('Expression 25', () => { - const E = TemplateLiteralParser.Parse('A(B)|(C)') - const R = [...TemplateLiteralGenerator.Generate(E)] + const E = TemplateLiteralParse('A(B)|(C)') + const R = [...TemplateLiteralGenerate(E)] Assert.IsEqual(R, ['AB', 'C']) }) it('Expression 26', () => { - const E = TemplateLiteralParser.Parse('A(B|C)') - const R = [...TemplateLiteralGenerator.Generate(E)] + const E = TemplateLiteralParse('A(B|C)') + const R = [...TemplateLiteralGenerate(E)] Assert.IsEqual(R, ['AB', 'AC']) }) it('Expression 27', () => { - const E = TemplateLiteralParser.Parse('A|(B|C)') - const R = [...TemplateLiteralGenerator.Generate(E)] + const E = TemplateLiteralParse('A|(B|C)') + const R = [...TemplateLiteralGenerate(E)] Assert.IsEqual(R, ['A', 'B', 'C']) }) it('Expression 28', () => { - const E = TemplateLiteralParser.Parse('((A)B)C') - const R = [...TemplateLiteralGenerator.Generate(E)] + const E = TemplateLiteralParse('((A)B)C') + const R = [...TemplateLiteralGenerate(E)] Assert.IsEqual(R, ['ABC']) }) it('Expression 29', () => { - const E = TemplateLiteralParser.Parse('(0|1)(0|1)') - const R = [...TemplateLiteralGenerator.Generate(E)] + const E = TemplateLiteralParse('(0|1)(0|1)') + const R = [...TemplateLiteralGenerate(E)] Assert.IsEqual(R, ['00', '01', '10', '11']) }) it('Expression 30', () => { - const E = TemplateLiteralParser.Parse('(0|1)|(0|1)') - const R = [...TemplateLiteralGenerator.Generate(E)] + const E = TemplateLiteralParse('(0|1)|(0|1)') + const R = [...TemplateLiteralGenerate(E)] Assert.IsEqual(R, ['0', '1', '0', '1']) }) it('Expression 31', () => { - const E = TemplateLiteralParser.Parse('(0|(1|0)|1)') - const R = [...TemplateLiteralGenerator.Generate(E)] + const E = TemplateLiteralParse('(0|(1|0)|1)') + const R = [...TemplateLiteralGenerate(E)] Assert.IsEqual(R, ['0', '1', '0', '1']) }) it('Expression 32', () => { - const E = TemplateLiteralParser.Parse('(0(1|0)1)') - const R = [...TemplateLiteralGenerator.Generate(E)] + const E = TemplateLiteralParse('(0(1|0)1)') + const R = [...TemplateLiteralGenerate(E)] Assert.IsEqual(R, ['011', '001']) }) }) diff --git a/test/runtime/type/template/parser.ts b/test/runtime/type/template/parser.ts index 5ab50afbf..4304b9375 100644 --- a/test/runtime/type/template/parser.ts +++ b/test/runtime/type/template/parser.ts @@ -1,4 +1,4 @@ -import { TemplateLiteralParser } from '@sinclair/typebox' +import { TemplateLiteralParse } from '@sinclair/typebox' import { Assert } from '../../assert/index' describe('type/templateliteral/TemplateLiteralParser', () => { @@ -6,23 +6,23 @@ describe('type/templateliteral/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/value/guard.ts b/test/runtime/type/value/guard.ts index 51652350b..d68d18952 100644 --- a/test/runtime/type/value/guard.ts +++ b/test/runtime/type/value/guard.ts @@ -2,6 +2,17 @@ import { Assert } from '../../assert/index' import { ValueGuard } from '@sinclair/typebox' describe('type/ValueGuard', () => { + // ----------------------------------------------------- + // IsSymbol + // ----------------------------------------------------- + it('Should guard symbol 1', () => { + const R = ValueGuard.IsSymbol(Symbol()) + Assert.IsTrue(R) + }) + it('Should guard symbol 2', () => { + const R = ValueGuard.IsSymbol({}) + Assert.IsFalse(R) + }) // ----------------------------------------------------- // IsNull // ----------------------------------------------------- @@ -106,4 +117,71 @@ describe('type/ValueGuard', () => { const R = ValueGuard.IsArray({}) Assert.IsFalse(R) }) + // ----------------------------------------------------- + // IsFunction + // ----------------------------------------------------- + it('Should guard function 1', () => { + const R = ValueGuard.IsFunction(function () {}) + Assert.IsTrue(R) + }) + it('Should guard function 2', () => { + const R = ValueGuard.IsFunction({}) + Assert.IsFalse(R) + }) + // ----------------------------------------------------- + // IsAsyncIterator + // ----------------------------------------------------- + it('Should guard async iterator 1', () => { + const R = ValueGuard.IsAsyncIterator((async function* (): any {})()) + Assert.IsTrue(R) + }) + it('Should guard async iterator 2', () => { + const R = ValueGuard.IsAsyncIterator((function* (): any {})()) + Assert.IsFalse(R) + }) + // ----------------------------------------------------- + // IsAsyncIterator + // ----------------------------------------------------- + it('Should guard iterator 1', () => { + const R = ValueGuard.IsIterator((function* (): any {})()) + Assert.IsTrue(R) + }) + it('Should guard iterator 2', () => { + const R = ValueGuard.IsIterator((async function* (): any {})()) + Assert.IsFalse(R) + }) + it('Should guard iterator 3', () => { + const R = ValueGuard.IsIterator([]) + Assert.IsFalse(R) + }) + it('Should guard iterator 4', () => { + const R = ValueGuard.IsIterator(new Uint8Array()) + Assert.IsFalse(R) + }) + // ----------------------------------------------------- + // Date + // ----------------------------------------------------- + it('Should guard date 1', () => { + const R = ValueGuard.IsDate(new Date()) + Assert.IsTrue(R) + }) + it('Should guard date 2', () => { + const R = ValueGuard.IsDate({}) + Assert.IsFalse(R) + }) + // ----------------------------------------------------- + // Date + // ----------------------------------------------------- + it('Should guard uint8array 1', () => { + const R = ValueGuard.IsUint8Array(new Uint8Array()) + Assert.IsTrue(R) + }) + it('Should guard uint8array 2', () => { + const R = ValueGuard.IsUint8Array({}) + Assert.IsFalse(R) + }) + it('Should guard uint8array 2', () => { + const R = ValueGuard.IsUint8Array([]) + Assert.IsFalse(R) + }) }) diff --git a/test/runtime/value/check/const.ts b/test/runtime/value/check/const.ts new file mode 100644 index 000000000..4b3db9f44 --- /dev/null +++ b/test/runtime/value/check/const.ts @@ -0,0 +1,42 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/check/Const', () => { + it('Should validate 1', () => { + const T = Type.Const(1) + Assert.IsTrue(Value.Check(T, 1)) + }) + it('Should validate 2', () => { + const T = Type.Const('hello') + Assert.IsTrue(Value.Check(T, 'hello')) + }) + it('Should validate 3', () => { + const T = Type.Const(true) + Assert.IsTrue(Value.Check(T, true)) + }) + it('Should validate 4', () => { + const T = Type.Const({ x: 1, y: 2 }) + Assert.IsTrue(Value.Check(T, { x: 1, y: 2 })) + }) + it('Should validate 5', () => { + const T = Type.Const([1, 2, 3]) + Assert.IsTrue(Value.Check(T, [1, 2, 3])) + }) + it('Should validate 6', () => { + const T = Type.Const([1, true, 'hello']) + Assert.IsTrue(Value.Check(T, [1, true, 'hello'])) + }) + it('Should validate 7', () => { + const T = Type.Const({ + x: [1, 2, 3, 4], + y: { x: 1, y: 2, z: 3 }, + }) + Assert.IsTrue( + Value.Check(T, { + x: [1, 2, 3, 4], + y: { x: 1, y: 2, z: 3 }, + }), + ) + }) +}) diff --git a/test/runtime/value/check/index.ts b/test/runtime/value/check/index.ts index 7b51cf3bf..9012725f2 100644 --- a/test/runtime/value/check/index.ts +++ b/test/runtime/value/check/index.ts @@ -4,6 +4,7 @@ import './async-iterator' import './bigint' import './boolean' import './composite' +import './const' import './constructor' import './date' import './enum' diff --git a/test/runtime/value/clean/any.ts b/test/runtime/value/clean/any.ts new file mode 100644 index 000000000..9a49a2562 --- /dev/null +++ b/test/runtime/value/clean/any.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Any', () => { + it('Should clean 1', () => { + const T = Type.Any() + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/clean/array.ts b/test/runtime/value/clean/array.ts new file mode 100644 index 000000000..6e8a21844 --- /dev/null +++ b/test/runtime/value/clean/array.ts @@ -0,0 +1,21 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Array', () => { + it('Should clean 1', () => { + const T = Type.Any() + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) + it('Should clean 2', () => { + const T = Type.Array( + Type.Object({ + x: Type.Number(), + y: Type.Number(), + }), + ) + const R = Value.Clean(T, [undefined, null, { x: 1 }, { x: 1, y: 2 }, { x: 1, y: 2, z: 3 }]) + Assert.IsEqual(R, [undefined, null, { x: 1 }, { x: 1, y: 2 }, { x: 1, y: 2 }]) + }) +}) diff --git a/test/runtime/value/clean/async-iterator.ts b/test/runtime/value/clean/async-iterator.ts new file mode 100644 index 000000000..a067233b3 --- /dev/null +++ b/test/runtime/value/clean/async-iterator.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/AsyncIterator', () => { + it('Should clean 1', () => { + const T = Type.AsyncIterator(Type.Number()) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/clean/bigint.ts b/test/runtime/value/clean/bigint.ts new file mode 100644 index 000000000..cdd77d4de --- /dev/null +++ b/test/runtime/value/clean/bigint.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/BigInt', () => { + it('Should clean 1', () => { + const T = Type.BigInt() + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/clean/boolean.ts b/test/runtime/value/clean/boolean.ts new file mode 100644 index 000000000..7697d25c3 --- /dev/null +++ b/test/runtime/value/clean/boolean.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Boolean', () => { + it('Should clean 1', () => { + const T = Type.Boolean() + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/clean/composite.ts b/test/runtime/value/clean/composite.ts new file mode 100644 index 000000000..035f335a9 --- /dev/null +++ b/test/runtime/value/clean/composite.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Composite', () => { + it('Should clean 1', () => { + const T = Type.Composite([Type.Object({ x: Type.Number() }), Type.Object({ y: Type.Number() })]) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/clean/constructor.ts b/test/runtime/value/clean/constructor.ts new file mode 100644 index 000000000..4a4be492c --- /dev/null +++ b/test/runtime/value/clean/constructor.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Constructor', () => { + it('Should clean 1', () => { + const T = Type.Constructor([Type.Object({ x: Type.Number() }), Type.Object({ y: Type.Number() })], Type.Object({ z: Type.Number() })) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/clean/date.ts b/test/runtime/value/clean/date.ts new file mode 100644 index 000000000..8f777e4c3 --- /dev/null +++ b/test/runtime/value/clean/date.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Date', () => { + it('Should clean 1', () => { + const T = Type.Date() + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/clean/enum.ts b/test/runtime/value/clean/enum.ts new file mode 100644 index 000000000..88902047b --- /dev/null +++ b/test/runtime/value/clean/enum.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Enum', () => { + it('Should clean 1', () => { + const T = Type.Enum({ x: 1, y: 2 }) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/clean/function.ts b/test/runtime/value/clean/function.ts new file mode 100644 index 000000000..2bc5a4ee5 --- /dev/null +++ b/test/runtime/value/clean/function.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Function', () => { + it('Should clean 1', () => { + const T = Type.Function([Type.Object({ x: Type.Number() }), Type.Object({ y: Type.Number() })], Type.Object({ z: Type.Number() })) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/clean/index.ts b/test/runtime/value/clean/index.ts new file mode 100644 index 000000000..b290dd224 --- /dev/null +++ b/test/runtime/value/clean/index.ts @@ -0,0 +1,34 @@ +import './any' +import './array' +import './async-iterator' +import './bigint' +import './boolean' +import './composite' +import './constructor' +import './date' +import './enum' +import './function' +import './integer' +import './intersect' +import './iterator' +import './keyof' +import './kind' +import './literal' +import './never' +import './not' +import './null' +import './object' +import './promise' +import './record' +import './recursive' +import './ref' +import './regexp' +import './string' +import './symbol' +import './template-literal' +import './tuple' +import './uint8array' +import './undefined' +import './union' +import './unknown' +import './void' diff --git a/test/runtime/value/clean/integer.ts b/test/runtime/value/clean/integer.ts new file mode 100644 index 000000000..ff377049c --- /dev/null +++ b/test/runtime/value/clean/integer.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Integer', () => { + it('Should clean 1', () => { + const T = Type.Integer() + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/clean/intersect.ts b/test/runtime/value/clean/intersect.ts new file mode 100644 index 000000000..14f5d5221 --- /dev/null +++ b/test/runtime/value/clean/intersect.ts @@ -0,0 +1,346 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +// prettier-ignore +describe('value/clean/Intersect', () => { + // ---------------------------------------------------------------- + // Intersect + // ---------------------------------------------------------------- + it('Should clean 1', () => { + const T = Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }) + ]) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) + it('Should clean 2', () => { + const T = Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }) + ]) + const R = Value.Clean(T, {}) + Assert.IsEqual(R, {}) + }) + it('Should clean 3', () => { + const T = Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }) + ]) + const R = Value.Clean(T, { x: 1, y: 2 }) + Assert.IsEqual(R, { x: 1, y: 2 }) + }) + it('Should clean 4', () => { + const T = Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }) + ]) + const R = Value.Clean(T, { x: 1, y: 2, z: 3 }) + Assert.IsEqual(R, { x: 1, y: 2 }) + }) + // ---------------------------------------------------------------- + // Intersect Discard + // ---------------------------------------------------------------- + it('Should clean discard 1', () => { + const T = Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }) + ]) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) + it('Should clean discard 2', () => { + const T = Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }) + ]) + const R = Value.Clean(T, { u: null }) + Assert.IsEqual(R, {}) + }) + it('Should clean discard 3', () => { + const T = Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }) + ]) + const R = Value.Clean(T, { u: null, x: 1, y: 2 }) + Assert.IsEqual(R, { x: 1, y: 2 }) + }) + it('Should clean discard 4', () => { + const T = Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }) + ]) + const R = Value.Clean(T, { u: null, x: 1, y: 2 }) + Assert.IsEqual(R, { x: 1, y: 2 }) + }) + // ---------------------------------------------------------------- + // Intersect Deep + // ---------------------------------------------------------------- + it('Should clear intersect deep 1', () => { + const T = Type.Intersect([ + Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }) + ]), + Type.Intersect([ + Type.Object({ z: Type.Number() }), + Type.Object({ w: Type.Number() }) + ]) + ]) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) + it('Should clear intersect deep 2', () => { + const T = Type.Intersect([ + Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }) + ]), + Type.Intersect([ + Type.Object({ z: Type.Number() }), + Type.Object({ w: Type.Number() }) + ]) + ]) + const R = Value.Clean(T, {}) + Assert.IsEqual(R, {}) + }) + it('Should clear intersect deep 3', () => { + const T = Type.Intersect([ + Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }) + ]), + Type.Intersect([ + Type.Object({ z: Type.Number() }), + Type.Object({ w: Type.Number() }) + ]) + ]) + const R = Value.Clean(T, { x: 1 }) + Assert.IsEqual(R, { x: 1 }) + }) + it('Should clear intersect deep 4', () => { + const T = Type.Intersect([ + Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }) + ]), + Type.Intersect([ + Type.Object({ z: Type.Number() }), + Type.Object({ w: Type.Number() }) + ]) + ]) + const R = Value.Clean(T, { x: 1, y: 2 }) + Assert.IsEqual(R, { x: 1, y: 2 }) + }) + it('Should clear intersect deep 5', () => { + const T = Type.Intersect([ + Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }) + ]), + Type.Intersect([ + Type.Object({ z: Type.Number() }), + Type.Object({ w: Type.Number() }) + ]) + ]) + const R = Value.Clean(T, { x: 1, y: 2, z: 3 }) + Assert.IsEqual(R, { x: 1, y: 2, z: 3 }) + }) + it('Should clear intersect deep 6', () => { + const T = Type.Intersect([ + Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }) + ]), + Type.Intersect([ + Type.Object({ z: Type.Number() }), + Type.Object({ w: Type.Number() }) + ]) + ]) + const R = Value.Clean(T, { x: 1, y: 2, z: 3, w: 3 }) + Assert.IsEqual(R, { x: 1, y: 2, z: 3, w: 3 }) + }) + it('Should clear intersect deep 7', () => { + const T = Type.Intersect([ + Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }) + ]), + Type.Intersect([ + Type.Object({ z: Type.Number() }), + Type.Object({ w: Type.Number() }) + ]) + ]) + const R = Value.Clean(T, { x: 1, y: 2, z: 3, w: 3 }) + Assert.IsEqual(R, { x: 1, y: 2, z: 3, w: 3 }) + }) + // ---------------------------------------------------------------- + // Intersect Deep Discard + // ---------------------------------------------------------------- + it('Should clear intersect discard deep 1', () => { + const T = Type.Intersect([ + Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }) + ]), + Type.Intersect([ + Type.Object({ z: Type.Number() }), + Type.Object({ w: Type.Number() }) + ]) + ]) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) + it('Should clear intersect discard deep 2', () => { + const T = Type.Intersect([ + Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }) + ]), + Type.Intersect([ + Type.Object({ z: Type.Number() }), + Type.Object({ w: Type.Number() }) + ]) + ]) + const R = Value.Clean(T, { u: null }) + Assert.IsEqual(R, {}) + }) + it('Should clear intersect discard deep 3', () => { + const T = Type.Intersect([ + Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }) + ]), + Type.Intersect([ + Type.Object({ z: Type.Number() }), + Type.Object({ w: Type.Number() }) + ]) + ]) + const R = Value.Clean(T, { u: null, x: 1 }) + Assert.IsEqual(R, { x: 1 }) + }) + it('Should clear intersect discard deep 4', () => { + const T = Type.Intersect([ + Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }) + ]), + Type.Intersect([ + Type.Object({ z: Type.Number() }), + Type.Object({ w: Type.Number() }) + ]) + ]) + const R = Value.Clean(T, { u: null, x: 1, y: 2 }) + Assert.IsEqual(R, { x: 1, y: 2 }) + }) + it('Should clear intersect discard deep 5', () => { + const T = Type.Intersect([ + Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }) + ]), + Type.Intersect([ + Type.Object({ z: Type.Number() }), + Type.Object({ w: Type.Number() }) + ]) + ]) + const R = Value.Clean(T, { u: null, x: 1, y: 2, z: 3 }) + Assert.IsEqual(R, { x: 1, y: 2, z: 3 }) + }) + it('Should clear intersect discard deep 6', () => { + const T = Type.Intersect([ + Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }) + ]), + Type.Intersect([ + Type.Object({ z: Type.Number() }), + Type.Object({ w: Type.Number() }) + ]) + ]) + const R = Value.Clean(T, { u: null, x: 1, y: 2, z: 3, w: 3 }) + Assert.IsEqual(R, { x: 1, y: 2, z: 3, w: 3 }) + }) + it('Should clear intersect discard deep 7', () => { + const T = Type.Intersect([ + Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.Number() }) + ]), + Type.Intersect([ + Type.Object({ z: Type.Number() }), + Type.Object({ w: Type.Number() }) + ]) + ]) + const R = Value.Clean(T, { u: null, x: 1, y: 2, z: 3, w: 3, a: 1 }) + Assert.IsEqual(R, { x: 1, y: 2, z: 3, w: 3 }) + }) + // ---------------------------------------------------------------- + // Intersect Invalid + // ---------------------------------------------------------------- + it('Should clean intersect invalid 1', () => { + const T = Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.String() }) + ]) + const R = Value.Clean(T, { x: 1, y: 2 }) + Assert.IsEqual(R, { x: 1, y: 2 }) // types are ignored + }) + // ---------------------------------------------------------------- + // Intersect Unevaluted Properties + // ---------------------------------------------------------------- + it('Should clean intersect unevaluated properties 1', () => { + const T = Type.Intersect([ + Type.Object({ x: Type.Number() }), + Type.Object({ y: Type.String() }) + ], { + unevaluatedProperties: Type.String() + }) + const R = Value.Clean(T, { x: 1, y: 2, a: 1, b: '' }) + Assert.IsEqual(R, { x: 1, y: 2, b: '' }) + }) + // ---------------------------------------------------------------- + // Intersect Illogical + // ---------------------------------------------------------------- + it('Should clean illogical 1', () => { + const T = Type.Intersect([ + Type.Number(), + Type.Object({ x: Type.Number() }) + ]) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) + it('Should clean illogical 2', () => { + const T = Type.Intersect([ + Type.Number(), + Type.Object({ x: Type.Number() }) + ]) + const R = Value.Clean(T, 1) + Assert.IsEqual(R, 1) + }) + it('Should clean illogical 3', () => { + const T = Type.Intersect([ + Type.Number(), + Type.Object({ x: Type.Number() }) + ]) + const R = Value.Clean(T, {}) + Assert.IsEqual(R, {}) + }) + it('Should clean illogical 4', () => { + const T = Type.Intersect([ + Type.Number(), + Type.Object({ x: Type.Number() }) + ]) + const R = Value.Clean(T, { x: 1 }) + Assert.IsEqual(R, { x: 1 }) + }) + it('Should clean illogical 5', () => { + const T = Type.Intersect([ + Type.Number(), + Type.Object({ x: Type.Number() }), + ]) + const R = Value.Clean(T, { u: null, x: 1 }) + Assert.IsEqual(R, { u: null, x: 1 }) // u retained from Number + }) +}) diff --git a/test/runtime/value/clean/iterator.ts b/test/runtime/value/clean/iterator.ts new file mode 100644 index 000000000..ec19feb07 --- /dev/null +++ b/test/runtime/value/clean/iterator.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Iterator', () => { + it('Should clean 1', () => { + const T = Type.Iterator(Type.String()) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/clean/keyof.ts b/test/runtime/value/clean/keyof.ts new file mode 100644 index 000000000..911912992 --- /dev/null +++ b/test/runtime/value/clean/keyof.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/KeyOf', () => { + it('Should clean 1', () => { + const T = Type.KeyOf(Type.Object({ x: Type.Number(), y: Type.Number() })) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/clean/kind.ts b/test/runtime/value/clean/kind.ts new file mode 100644 index 000000000..8a4b836d5 --- /dev/null +++ b/test/runtime/value/clean/kind.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type, Kind } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Kind', () => { + it('Should clean 1', () => { + const T = Type.Unsafe({ [Kind]: 'Unknown' }) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/clean/literal.ts b/test/runtime/value/clean/literal.ts new file mode 100644 index 000000000..bf424b784 --- /dev/null +++ b/test/runtime/value/clean/literal.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Literal', () => { + it('Should clean 1', () => { + const T = Type.Literal(1) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/clean/never.ts b/test/runtime/value/clean/never.ts new file mode 100644 index 000000000..2945598e4 --- /dev/null +++ b/test/runtime/value/clean/never.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Never', () => { + it('Should clean 1', () => { + const T = Type.Never() + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/clean/not.ts b/test/runtime/value/clean/not.ts new file mode 100644 index 000000000..cb60a05d0 --- /dev/null +++ b/test/runtime/value/clean/not.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Not', () => { + it('Should clean 1', () => { + const T = Type.Not(Type.Any()) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/clean/null.ts b/test/runtime/value/clean/null.ts new file mode 100644 index 000000000..a52d81a4d --- /dev/null +++ b/test/runtime/value/clean/null.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Null', () => { + it('Should clean 1', () => { + const T = Type.Null() + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/clean/number.ts b/test/runtime/value/clean/number.ts new file mode 100644 index 000000000..aafd93083 --- /dev/null +++ b/test/runtime/value/clean/number.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Number', () => { + it('Should clean 1', () => { + const T = Type.Number() + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/clean/object.ts b/test/runtime/value/clean/object.ts new file mode 100644 index 000000000..805665a7e --- /dev/null +++ b/test/runtime/value/clean/object.ts @@ -0,0 +1,178 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Object', () => { + // ---------------------------------------------------------------- + // Clean + // ---------------------------------------------------------------- + it('Should clean 1', () => { + const T = Type.Object({ x: Type.Number() }) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) + it('Should clean 2', () => { + const T = Type.Object({ x: Type.Number() }) + const R = Value.Clean(T, {}) + Assert.IsEqual(R, {}) + }) + it('Should clean 3', () => { + const T = Type.Object({ x: Type.Number() }) + const R = Value.Clean(T, { x: 1 }) + Assert.IsEqual(R, { x: 1 }) + }) + it('Should clean 4', () => { + const T = Type.Object({ x: Type.Number() }) + const R = Value.Clean(T, { x: null }) + Assert.IsEqual(R, { x: null }) + }) + // ---------------------------------------------------------------- + // Nested + // ---------------------------------------------------------------- + it('Should clean nested 1', () => { + const T = Type.Object({ + x: Type.Object({ + y: Type.Number(), + }), + }) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) + it('Should clean nested 2', () => { + const T = Type.Object({ + x: Type.Object({ + y: Type.Number(), + }), + }) + const R = Value.Clean(T, {}) + Assert.IsEqual(R, {}) + }) + it('Should clean nested 3', () => { + const T = Type.Object({ + x: Type.Object({ + y: Type.Number(), + }), + }) + const R = Value.Clean(T, { x: null }) + Assert.IsEqual(R, { x: null }) + }) + it('Should clean nested 4', () => { + const T = Type.Object({ + x: Type.Object({ + y: Type.Number(), + }), + }) + const R = Value.Clean(T, { x: { y: null } }) + Assert.IsEqual(R, { x: { y: null } }) + }) + // ---------------------------------------------------------------- + // Additional Properties + // ---------------------------------------------------------------- + it('Should clean additional properties 1', () => { + const T = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + }, + { + additionalProperties: Type.String(), + }, + ) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) + it('Should clean additional properties 2', () => { + const T = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + }, + { + additionalProperties: Type.String(), + }, + ) + const R = Value.Clean(T, {}) + Assert.IsEqual(R, {}) + }) + it('Should clean additional properties 3', () => { + const T = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + }, + { + additionalProperties: Type.String(), + }, + ) + const R = Value.Clean(T, { x: 1 }) + Assert.IsEqual(R, { x: 1 }) + }) + it('Should clean additional properties 4', () => { + const T = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + }, + { + additionalProperties: Type.String(), + }, + ) + const R = Value.Clean(T, { x: 1, y: 2 }) + Assert.IsEqual(R, { x: 1, y: 2 }) + }) + // ---------------------------------------------------------------- + // Additional Properties Discard + // ---------------------------------------------------------------- + it('Should clean additional properties discard 1', () => { + const T = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + }, + { + additionalProperties: Type.String(), + }, + ) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) + it('Should clean additional properties discard 2', () => { + const T = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + }, + { + additionalProperties: Type.String(), + }, + ) + const R = Value.Clean(T, { k: '', d: null }) + Assert.IsEqual(R, { k: '' }) + }) + it('Should clean additional properties discard 3', () => { + const T = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + }, + { + additionalProperties: Type.String(), + }, + ) + const R = Value.Clean(T, { k: '', d: null, x: 1 }) + Assert.IsEqual(R, { k: '', x: 1 }) + }) + it('Should clean additional properties discard 4', () => { + const T = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + }, + { + additionalProperties: Type.String(), + }, + ) + const R = Value.Clean(T, { k: '', d: null, x: 1, y: 2 }) + Assert.IsEqual(R, { k: '', x: 1, y: 2 }) + }) +}) diff --git a/test/runtime/value/clean/promise.ts b/test/runtime/value/clean/promise.ts new file mode 100644 index 000000000..0d2d7e4ff --- /dev/null +++ b/test/runtime/value/clean/promise.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Promise', () => { + it('Should clean 1', () => { + const T = Type.Promise(Type.Any()) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/clean/record.ts b/test/runtime/value/clean/record.ts new file mode 100644 index 000000000..6ef341c15 --- /dev/null +++ b/test/runtime/value/clean/record.ts @@ -0,0 +1,114 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Record', () => { + // ---------------------------------------------------------------- + // Clean + // ---------------------------------------------------------------- + it('Should clean 1', () => { + const T = Type.Record(Type.Number(), Type.String()) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) + it('Should clean 2', () => { + const T = Type.Record(Type.Number(), Type.String()) + const R = Value.Clean(T, {}) + Assert.IsEqual(R, {}) + }) + it('Should clean 3', () => { + const T = Type.Record(Type.Number(), Type.String()) + const R = Value.Clean(T, { 0: null }) + Assert.IsEqual(R, { 0: null }) + }) + // ---------------------------------------------------------------- + // Clean Discard + // ---------------------------------------------------------------- + it('Should clean discard 1', () => { + const T = Type.Record(Type.Number(), Type.String()) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) + it('Should clean discard 2', () => { + const T = Type.Record(Type.Number(), Type.String()) + const R = Value.Clean(T, { a: 1 }) + Assert.IsEqual(R, {}) + }) + it('Should clean discard 3', () => { + const T = Type.Record(Type.Number(), Type.String()) + const R = Value.Clean(T, { a: 1, 0: null }) + Assert.IsEqual(R, { 0: null }) + }) + // ---------------------------------------------------------------- + // Additional Properties + // ---------------------------------------------------------------- + it('Should clean additional properties 1', () => { + const T = Type.Record(Type.Number(), Type.String(), { + additionalProperties: Type.Boolean(), + }) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) + it('Should clean additional properties 2', () => { + const T = Type.Record(Type.Number(), Type.String(), { + additionalProperties: Type.Boolean(), + }) + const R = Value.Clean(T, {}) + Assert.IsEqual(R, {}) + }) + it('Should clean additional properties 3', () => { + const T = Type.Record(Type.Number(), Type.String(), { + additionalProperties: Type.Boolean(), + }) + const R = Value.Clean(T, { 0: null }) + Assert.IsEqual(R, { 0: null }) + }) + // ---------------------------------------------------------------- + // Additional Properties Discard + // ---------------------------------------------------------------- + it('Should clean additional properties discard 1', () => { + const T = Type.Record(Type.Number(), Type.String(), { + additionalProperties: Type.Boolean(), + }) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) + it('Should clean additional properties discard 2', () => { + const T = Type.Record(Type.Number(), Type.String(), { + additionalProperties: Type.Boolean(), + }) + const R = Value.Clean(T, { a: null }) + Assert.IsEqual(R, {}) + }) + it('Should clean additional properties discard 3', () => { + const T = Type.Record(Type.Number(), Type.String(), { + additionalProperties: Type.Boolean(), + }) + const R = Value.Clean(T, { a: null, 0: null }) + Assert.IsEqual(R, { 0: null }) + }) + // ---------------------------------------------------------------- + // Additional Properties Keep + // ---------------------------------------------------------------- + it('Should clean additional properties keep 1', () => { + const T = Type.Record(Type.Number(), Type.String(), { + additionalProperties: Type.Boolean(), + }) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) + it('Should clean additional properties keep 2', () => { + const T = Type.Record(Type.Number(), Type.String(), { + additionalProperties: Type.Boolean(), + }) + const R = Value.Clean(T, { a: true }) + Assert.IsEqual(R, { a: true }) + }) + it('Should clean additional properties keep 3', () => { + const T = Type.Record(Type.Number(), Type.String(), { + additionalProperties: Type.Boolean(), + }) + const R = Value.Clean(T, { a: true, 0: null }) + Assert.IsEqual(R, { a: true, 0: null }) + }) +}) diff --git a/test/runtime/value/clean/recursive.ts b/test/runtime/value/clean/recursive.ts new file mode 100644 index 000000000..1a6bb6e82 --- /dev/null +++ b/test/runtime/value/clean/recursive.ts @@ -0,0 +1,112 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Recursive', () => { + // ---------------------------------------------------------------- + // Clean + // ---------------------------------------------------------------- + it('Should clean 1', () => { + const T = Type.Recursive((This) => + Type.Object({ + id: Type.String(), + nodes: Type.Array(This), + }), + ) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) + it('Should clean 2', () => { + const T = Type.Recursive((This) => + Type.Object({ + id: Type.String(), + nodes: Type.Array(This), + }), + ) + const R = Value.Clean(T, { id: null }) + Assert.IsEqual(R, { id: null }) + }) + it('Should clean 3', () => { + const T = Type.Recursive((This) => + Type.Object({ + id: Type.String(), + nodes: Type.Array(This), + }), + ) + const R = Value.Clean(T, { id: null, nodes: null }) + Assert.IsEqual(R, { id: null, nodes: null }) + }) + it('Should clean 4', () => { + const T = Type.Recursive((This) => + Type.Object({ + id: Type.String(), + nodes: Type.Array(This), + }), + ) + const R = Value.Clean(T, { id: null, nodes: [] }) + Assert.IsEqual(R, { id: null, nodes: [] }) + }) + it('Should clean 5', () => { + const T = Type.Recursive((This) => + Type.Object({ + id: Type.String(), + nodes: Type.Array(This), + }), + ) + const R = Value.Clean(T, { id: null, nodes: [{ id: null }] }) + Assert.IsEqual(R, { id: null, nodes: [{ id: null }] }) + }) + // ---------------------------------------------------------------- + // Clean Discard + // ---------------------------------------------------------------- + it('Should clean discard 1', () => { + const T = Type.Recursive((This) => + Type.Object({ + id: Type.String(), + nodes: Type.Array(This), + }), + ) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) + it('Should clean discard 2', () => { + const T = Type.Recursive((This) => + Type.Object({ + id: Type.String(), + nodes: Type.Array(This), + }), + ) + const R = Value.Clean(T, { u: null, id: null }) + Assert.IsEqual(R, { id: null }) + }) + it('Should clean discard 3', () => { + const T = Type.Recursive((This) => + Type.Object({ + id: Type.String(), + nodes: Type.Array(This), + }), + ) + const R = Value.Clean(T, { u: null, id: null, nodes: null }) + Assert.IsEqual(R, { id: null, nodes: null }) + }) + it('Should clean discard 4', () => { + const T = Type.Recursive((This) => + Type.Object({ + id: Type.String(), + nodes: Type.Array(This), + }), + ) + const R = Value.Clean(T, { u: null, id: null, nodes: [] }) + Assert.IsEqual(R, { id: null, nodes: [] }) + }) + it('Should clean discard 5', () => { + const T = Type.Recursive((This) => + Type.Object({ + id: Type.String(), + nodes: Type.Array(This), + }), + ) + const R = Value.Clean(T, { u: null, id: null, nodes: [{ u: null, id: null }] }) + Assert.IsEqual(R, { id: null, nodes: [{ id: null }] }) + }) +}) diff --git a/test/runtime/value/clean/ref.ts b/test/runtime/value/clean/ref.ts new file mode 100644 index 000000000..b06556159 --- /dev/null +++ b/test/runtime/value/clean/ref.ts @@ -0,0 +1,78 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Ref', () => { + // ---------------------------------------------------------------- + // Clean + // ---------------------------------------------------------------- + it('Should clean 1', () => { + const A = Type.Object( + { + x: Type.Number(), + }, + { $id: 'A' }, + ) + const T = Type.Ref('A') + const R = Value.Clean(T, [A], null) + Assert.IsEqual(R, null) + }) + it('Should clean 2', () => { + const A = Type.Object( + { + x: Type.Number(), + }, + { $id: 'A' }, + ) + const T = Type.Ref('A') + const R = Value.Clean(T, [A], {}) + Assert.IsEqual(R, {}) + }) + it('Should clean 3', () => { + const A = Type.Object( + { + x: Type.Number(), + }, + { $id: 'A' }, + ) + const T = Type.Ref('A') + const R = Value.Clean(T, [A], { x: null }) + Assert.IsEqual(R, { x: null }) + }) + // ---------------------------------------------------------------- + // Clean Discard + // ---------------------------------------------------------------- + it('Should clean discard 1', () => { + const A = Type.Object( + { + x: Type.Number(), + }, + { $id: 'A' }, + ) + const T = Type.Ref('A') + const R = Value.Clean(T, [A], null) + Assert.IsEqual(R, null) + }) + it('Should clean discard 2', () => { + const A = Type.Object( + { + x: Type.Number(), + }, + { $id: 'A' }, + ) + const T = Type.Ref('A') + const R = Value.Clean(T, [A], { a: null }) + Assert.IsEqual(R, {}) + }) + it('Should clean discard 3', () => { + const A = Type.Object( + { + x: Type.Number(), + }, + { $id: 'A' }, + ) + const T = Type.Ref('A') + const R = Value.Clean(T, [A], { a: null, x: null }) + Assert.IsEqual(R, { x: null }) + }) +}) diff --git a/test/runtime/value/clean/regexp.ts b/test/runtime/value/clean/regexp.ts new file mode 100644 index 000000000..6b1d57421 --- /dev/null +++ b/test/runtime/value/clean/regexp.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/RegExp', () => { + it('Should clean 1', () => { + const T = Type.RegExp('') + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/clean/string.ts b/test/runtime/value/clean/string.ts new file mode 100644 index 000000000..aa5d75727 --- /dev/null +++ b/test/runtime/value/clean/string.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/String', () => { + it('Should clean 1', () => { + const T = Type.String() + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/clean/symbol.ts b/test/runtime/value/clean/symbol.ts new file mode 100644 index 000000000..62047d28e --- /dev/null +++ b/test/runtime/value/clean/symbol.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Symbol', () => { + it('Should clean 1', () => { + const T = Type.Symbol() + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/clean/template-literal.ts b/test/runtime/value/clean/template-literal.ts new file mode 100644 index 000000000..f76730548 --- /dev/null +++ b/test/runtime/value/clean/template-literal.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/TemplateLiteral', () => { + it('Should clean 1', () => { + const T = Type.TemplateLiteral('') + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/clean/tuple.ts b/test/runtime/value/clean/tuple.ts new file mode 100644 index 000000000..81618f0f9 --- /dev/null +++ b/test/runtime/value/clean/tuple.ts @@ -0,0 +1,110 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Tuple', () => { + // ---------------------------------------------------------------- + // Clean + // ---------------------------------------------------------------- + it('Should clean 1', () => { + const T = Type.Tuple([Type.Number(), Type.Number()]) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) + it('Should clean 2', () => { + const T = Type.Tuple([Type.Number(), Type.Number()]) + const R = Value.Clean(T, []) + Assert.IsEqual(R, []) + }) + it('Should clean 3', () => { + const T = Type.Tuple([Type.Number(), Type.Number()]) + const R = Value.Clean(T, [1, 2]) + Assert.IsEqual(R, [1, 2]) + }) + it('Should clean 4', () => { + const T = Type.Tuple([Type.Number(), Type.Number()]) + const R = Value.Clean(T, [1, 2, 3]) + Assert.IsEqual(R, [1, 2]) + }) + // ---------------------------------------------------------------- + // Clean Deep + // ---------------------------------------------------------------- + it('Should clean deep 1', () => { + const T = Type.Tuple([ + Type.Number(), + Type.Object({ + x: Type.Number(), + }), + ]) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) + it('Should clean deep 2', () => { + const T = Type.Tuple([ + Type.Number(), + Type.Object({ + x: Type.Number(), + }), + ]) + const R = Value.Clean(T, []) + Assert.IsEqual(R, []) + }) + it('Should clean deep 3', () => { + const T = Type.Tuple([ + Type.Number(), + Type.Object({ + x: Type.Number(), + }), + ]) + const R = Value.Clean(T, [1]) + Assert.IsEqual(R, [1]) + }) + it('Should clean deep 4', () => { + const T = Type.Tuple([ + Type.Number(), + Type.Object({ + x: Type.Number(), + }), + ]) + const R = Value.Clean(T, [1, null]) + Assert.IsEqual(R, [1, null]) + }) + it('Should clean deep 5', () => { + const T = Type.Tuple([ + Type.Number(), + Type.Object({ + x: Type.Number(), + }), + ]) + const R = Value.Clean(T, [1, { x: null }]) + Assert.IsEqual(R, [1, { x: null }]) + }) + it('Should clean deep 6', () => { + const T = Type.Tuple([ + Type.Number(), + Type.Object({ + x: Type.Number(), + }), + ]) + const R = Value.Clean(T, [1, { u: null, x: null }]) + Assert.IsEqual(R, [1, { x: null }]) + }) + // ---------------------------------------------------------------- + // Clean Empty + // ---------------------------------------------------------------- + it('Should clean empty 1', () => { + const T = Type.Tuple([]) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) + it('Should clean empty 2', () => { + const T = Type.Tuple([]) + const R = Value.Clean(T, []) + Assert.IsEqual(R, []) + }) + it('Should clean empty 3', () => { + const T = Type.Tuple([]) + const R = Value.Clean(T, [1]) + Assert.IsEqual(R, []) + }) +}) diff --git a/test/runtime/value/clean/uint8array.ts b/test/runtime/value/clean/uint8array.ts new file mode 100644 index 000000000..732eb5b71 --- /dev/null +++ b/test/runtime/value/clean/uint8array.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Uint8Array', () => { + it('Should clean 1', () => { + const T = Type.Uint8Array() + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/clean/undefined.ts b/test/runtime/value/clean/undefined.ts new file mode 100644 index 000000000..4b590c41b --- /dev/null +++ b/test/runtime/value/clean/undefined.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Undefined', () => { + it('Should clean 1', () => { + const T = Type.Undefined() + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/clean/union.ts b/test/runtime/value/clean/union.ts new file mode 100644 index 000000000..443e3d651 --- /dev/null +++ b/test/runtime/value/clean/union.ts @@ -0,0 +1,134 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Union', () => { + // ---------------------------------------------------------------- + // Clean + // ---------------------------------------------------------------- + it('Should clean 1', () => { + const T = Type.Union([Type.Number(), Type.Boolean()]) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) + it('Should clean 2', () => { + const T = Type.Union([Type.Number(), Type.Boolean()]) + const R = Value.Clean(T, 1) + Assert.IsEqual(R, 1) + }) + it('Should clean 2', () => { + const T = Type.Union([Type.Number(), Type.Boolean()]) + const R = Value.Clean(T, true) + Assert.IsEqual(R, true) + }) + // ---------------------------------------------------------------- + // Clean Select + // ---------------------------------------------------------------- + it('Should clean select 1', () => { + const X = Type.Object({ x: Type.Number() }) + const Y = Type.Object({ y: Type.Number() }) + const T = Type.Union([X, Y]) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) + it('Should clean select 2', () => { + const X = Type.Object({ x: Type.Number() }) + const Y = Type.Object({ y: Type.Number() }) + const T = Type.Union([X, Y]) + const R = Value.Clean(T, {}) + Assert.IsEqual(R, {}) + }) + it('Should clean select 3', () => { + const X = Type.Object({ x: Type.Number() }) + const Y = Type.Object({ y: Type.Number() }) + const T = Type.Union([X, Y]) + const R = Value.Clean(T, { x: null }) + Assert.IsEqual(R, { x: null }) + }) + it('Should clean select 4', () => { + const X = Type.Object({ x: Type.Number() }) + const Y = Type.Object({ y: Type.Number() }) + const T = Type.Union([X, Y]) + const R = Value.Clean(T, { y: null }) + Assert.IsEqual(R, { y: null }) + }) + // ---------------------------------------------------------------- + // Clean Select Discard + // ---------------------------------------------------------------- + it('Should clean select discard 1', () => { + const X = Type.Object({ x: Type.Number() }) + const Y = Type.Object({ y: Type.Number() }) + const T = Type.Union([X, Y]) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) + it('Should clean select discard 2', () => { + const X = Type.Object({ x: Type.Number() }) + const Y = Type.Object({ y: Type.Number() }) + const T = Type.Union([X, Y]) + const R = Value.Clean(T, { u: null }) + Assert.IsEqual(R, { u: null }) // no match + }) + it('Should clean select discard 3', () => { + const X = Type.Object({ x: Type.Number() }) + const Y = Type.Object({ y: Type.Number() }) + const T = Type.Union([X, Y]) + const R = Value.Clean(T, { u: null, x: 1 }) + Assert.IsEqual(R, { x: 1 }) + }) + it('Should clean select discard 4', () => { + const X = Type.Object({ x: Type.Number() }) + const Y = Type.Object({ y: Type.Number() }) + const T = Type.Union([X, Y]) + const R = Value.Clean(T, { u: null, y: 1 }) + Assert.IsEqual(R, { y: 1 }) + }) + // ---------------------------------------------------------------- + // Clean Select Retain + // ---------------------------------------------------------------- + it('Should clean select retain 1', () => { + const X = Type.Object({ x: Type.Number() }, { additionalProperties: Type.Null() }) + const Y = Type.Object({ y: Type.Number() }, { additionalProperties: Type.Null() }) + const T = Type.Union([X, Y]) + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) + it('Should clean select retain 2', () => { + const X = Type.Object({ x: Type.Number() }, { additionalProperties: Type.Null() }) + const Y = Type.Object({ y: Type.Number() }, { additionalProperties: Type.Null() }) + const T = Type.Union([X, Y]) + const R = Value.Clean(T, { u: null }) + Assert.IsEqual(R, { u: null }) + }) + it('Should clean select retain 3', () => { + const X = Type.Object({ x: Type.Number() }, { additionalProperties: Type.Null() }) + const Y = Type.Object({ y: Type.Number() }, { additionalProperties: Type.Null() }) + const T = Type.Union([X, Y]) + const R = Value.Clean(T, { u: null, x: 1 }) + Assert.IsEqual(R, { u: null, x: 1 }) + }) + it('Should clean select retain 4', () => { + const X = Type.Object({ x: Type.Number() }, { additionalProperties: Type.Null() }) + const Y = Type.Object({ y: Type.Number() }, { additionalProperties: Type.Null() }) + const T = Type.Union([X, Y]) + const R = Value.Clean(T, { u: null, y: 1 }) + Assert.IsEqual(R, { u: null, y: 1 }) + }) + // ---------------------------------------------------------------- + // Clean Select First and Discard + // ---------------------------------------------------------------- + it('Should clean select first and discard 1', () => { + const X = Type.Object({ x: Type.Number() }) + const Y = Type.Object({ y: Type.Number() }, { additionalProperties: Type.Null() }) + const T = Type.Union([X, Y]) + const R = Value.Clean(T, { u: null, x: 1 }) + Assert.IsEqual(R, { x: 1 }) + }) + it('Should clean select first and discard 2', () => { + const X = Type.Object({ x: Type.Number() }) + const Y = Type.Object({ y: Type.Number() }, { additionalProperties: Type.Null() }) + const T = Type.Union([X, Y]) + const R = Value.Clean(T, { u: null, y: 1 }) + Assert.IsEqual(R, { u: null, y: 1 }) + }) +}) diff --git a/test/runtime/value/clean/unknown.ts b/test/runtime/value/clean/unknown.ts new file mode 100644 index 000000000..edba3ebfc --- /dev/null +++ b/test/runtime/value/clean/unknown.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Unknown', () => { + it('Should clean 1', () => { + const T = Type.Unknown() + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/clean/void.ts b/test/runtime/value/clean/void.ts new file mode 100644 index 000000000..cd4c20db0 --- /dev/null +++ b/test/runtime/value/clean/void.ts @@ -0,0 +1,11 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/clean/Void', () => { + it('Should clean 1', () => { + const T = Type.Void() + const R = Value.Clean(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/default/any.ts b/test/runtime/value/default/any.ts new file mode 100644 index 000000000..15b12e979 --- /dev/null +++ b/test/runtime/value/default/any.ts @@ -0,0 +1,16 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/Any', () => { + it('Should use default', () => { + const T = Type.Any({ default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.Any({ default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/default/array.ts b/test/runtime/value/default/array.ts new file mode 100644 index 000000000..be385e603 --- /dev/null +++ b/test/runtime/value/default/array.ts @@ -0,0 +1,24 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/Array', () => { + it('Should use default', () => { + const T = Type.Array(Type.Number(), { default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.Array(Type.Number(), { default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) + // ---------------------------------------------------------------- + // Elements + // ---------------------------------------------------------------- + it('Should use default on elements', () => { + const T = Type.Array(Type.Number({ default: 2 })) + const R = Value.Default(T, [1, undefined, 3]) + Assert.IsEqual(R, [1, 2, 3]) + }) +}) diff --git a/test/runtime/value/default/async-iterator.ts b/test/runtime/value/default/async-iterator.ts new file mode 100644 index 000000000..dd26f579c --- /dev/null +++ b/test/runtime/value/default/async-iterator.ts @@ -0,0 +1,16 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/AsyncIterator', () => { + it('Should use default', () => { + const T = Type.AsyncIterator(Type.Number(), { default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.AsyncIterator(Type.Number(), { default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/default/bigint.ts b/test/runtime/value/default/bigint.ts new file mode 100644 index 000000000..b7e8aa710 --- /dev/null +++ b/test/runtime/value/default/bigint.ts @@ -0,0 +1,16 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/BigInt', () => { + it('Should use default', () => { + const T = Type.BigInt({ default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.BigInt({ default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/default/boolean.ts b/test/runtime/value/default/boolean.ts new file mode 100644 index 000000000..ee05bb06b --- /dev/null +++ b/test/runtime/value/default/boolean.ts @@ -0,0 +1,16 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/Boolean', () => { + it('Should use default', () => { + const T = Type.Boolean({ default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.Boolean({ default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/default/composite.ts b/test/runtime/value/default/composite.ts new file mode 100644 index 000000000..8a73e4106 --- /dev/null +++ b/test/runtime/value/default/composite.ts @@ -0,0 +1,16 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/Composite', () => { + it('Should use default', () => { + const T = Type.Composite([Type.Object({ x: Type.Number() }), Type.Object({ y: Type.Number() })], { default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.Composite([Type.Object({ x: Type.Number() }), Type.Object({ y: Type.Number() })], { default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/default/constructor.ts b/test/runtime/value/default/constructor.ts new file mode 100644 index 000000000..76cb2343f --- /dev/null +++ b/test/runtime/value/default/constructor.ts @@ -0,0 +1,16 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/Constructor', () => { + it('Should use default', () => { + const T = Type.Constructor([], Type.Number(), { default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.Constructor([], Type.Number(), { default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/default/date.ts b/test/runtime/value/default/date.ts new file mode 100644 index 000000000..2522cf20d --- /dev/null +++ b/test/runtime/value/default/date.ts @@ -0,0 +1,16 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/Date', () => { + it('Should use default', () => { + const T = Type.Date({ default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.Date({ default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/default/enum.ts b/test/runtime/value/default/enum.ts new file mode 100644 index 000000000..ae6f9ece2 --- /dev/null +++ b/test/runtime/value/default/enum.ts @@ -0,0 +1,16 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/Enum', () => { + it('Should use default', () => { + const T = Type.Enum({}, { default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.Enum({}, { default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/default/function.ts b/test/runtime/value/default/function.ts new file mode 100644 index 000000000..959c28284 --- /dev/null +++ b/test/runtime/value/default/function.ts @@ -0,0 +1,16 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/Function', () => { + it('Should use default', () => { + const T = Type.Function([], Type.Number(), { default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.Function([], Type.Number(), { default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/default/index.ts b/test/runtime/value/default/index.ts new file mode 100644 index 000000000..7395f9fce --- /dev/null +++ b/test/runtime/value/default/index.ts @@ -0,0 +1,35 @@ +import './any' +import './array' +import './async-iterator' +import './bigint' +import './boolean' +import './composite' +import './constructor' +import './date' +import './enum' +import './function' +import './integer' +import './intersect' +import './iterator' +import './keyof' +import './kind' +import './literal' +import './never' +import './not' +import './null' +import './number' +import './object' +import './promise' +import './record' +import './recursive' +import './ref' +import './regexp' +import './string' +import './symbol' +import './template-literal' +import './tuple' +import './uint8array' +import './undefined' +import './union' +import './unknown' +import './void' diff --git a/test/runtime/value/default/integer.ts b/test/runtime/value/default/integer.ts new file mode 100644 index 000000000..c0ce64b4a --- /dev/null +++ b/test/runtime/value/default/integer.ts @@ -0,0 +1,16 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/Integer', () => { + it('Should use default', () => { + const T = Type.Integer({ default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.Integer({ default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/default/intersect.ts b/test/runtime/value/default/intersect.ts new file mode 100644 index 000000000..0e72b3e6e --- /dev/null +++ b/test/runtime/value/default/intersect.ts @@ -0,0 +1,106 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/Intersect', () => { + it('Should use default', () => { + const T = Type.Intersect([Type.Number(), Type.String()], { default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.Intersect([Type.Number(), Type.String()], { default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) + // ---------------------------------------------------------------- + // Intersected + // ---------------------------------------------------------------- + it('Should use default intersected 1', () => { + const A = Type.Object({ + a: Type.Number({ default: 1 }), + }) + const B = Type.Object({ + b: Type.Number({ default: 2 }), + }) + const T = Type.Intersect([A, B]) + const R = Value.Default(T, {}) + Assert.IsEqual(R, { a: 1, b: 2 }) + }) + it('Should use default intersected 2', () => { + const A = Type.Object({ + a: Type.Number(), + }) + const B = Type.Object({ + b: Type.Number(), + }) + const T = Type.Intersect([A, B]) + const R = Value.Default(T, {}) + Assert.IsEqual(R, {}) + }) + it('Should use default intersected 3', () => { + const A = Type.Object({ + a: Type.Number({ default: 1 }), + }) + const B = Type.Object({ + b: Type.Number({ default: 2 }), + }) + const T = Type.Intersect([A, B]) + const R = Value.Default(T, { a: 3 }) + Assert.IsEqual(R, { a: 3, b: 2 }) + }) + it('Should use default intersected 4', () => { + const A = Type.Object({ + a: Type.Number({ default: 1 }), + }) + const B = Type.Object({ + b: Type.Number({ default: 2 }), + }) + const T = Type.Intersect([A, B]) + const R = Value.Default(T, { a: 4, b: 5 }) + Assert.IsEqual(R, { a: 4, b: 5 }) + }) + it('Should use default intersected 5', () => { + const A = Type.Object({ + a: Type.Number({ default: 1 }), + }) + const B = Type.Number({ default: 2 }) + const T = Type.Intersect([A, B]) + const R = Value.Default(T, {}) + Assert.IsEqual(R, { a: 1 }) + }) + it('Should use default intersected 6', () => { + const A = Type.Number({ default: 2 }) + const B = Type.Object({ + a: Type.Number({ default: 1 }), + }) + const T = Type.Intersect([A, B]) + const R = Value.Default(T, {}) + Assert.IsEqual(R, { a: 1 }) + }) + // ---------------------------------------------------------------- + // Intersected Deep + // ---------------------------------------------------------------- + it('Should use default intersected deep 1', () => { + const A = Type.Object({ a: Type.Number({ default: 1 }) }) + const B = Type.Object({ b: Type.Number({ default: 2 }) }) + const C = Type.Object({ c: Type.Number({ default: 3 }) }) + const D = Type.Object({ d: Type.Number({ default: 4 }) }) + const T1 = Type.Intersect([A, B]) + const T2 = Type.Intersect([C, D]) + const T = Type.Intersect([T1, T2]) + const R = Value.Default(T, {}) + Assert.IsEqual(R, { a: 1, b: 2, c: 3, d: 4 }) + }) + it('Should use default intersected deep 2', () => { + const A = Type.Object({ a: Type.Number({}) }) + const B = Type.Object({ b: Type.Number({}) }) + const C = Type.Object({ c: Type.Number({}) }) + const D = Type.Object({ d: Type.Number({}) }) + const T1 = Type.Intersect([A, B]) + const T2 = Type.Intersect([C, D]) + const T = Type.Intersect([T1, T2]) + const R = Value.Default(T, {}) + Assert.IsEqual(R, {}) + }) +}) diff --git a/test/runtime/value/default/iterator.ts b/test/runtime/value/default/iterator.ts new file mode 100644 index 000000000..d49b7f296 --- /dev/null +++ b/test/runtime/value/default/iterator.ts @@ -0,0 +1,16 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/Iterator', () => { + it('Should use default', () => { + const T = Type.Iterator(Type.Number(), { default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.Iterator(Type.Number(), { default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/default/keyof.ts b/test/runtime/value/default/keyof.ts new file mode 100644 index 000000000..2cb0bb26b --- /dev/null +++ b/test/runtime/value/default/keyof.ts @@ -0,0 +1,16 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/KeyOf', () => { + it('Should use default', () => { + const T = Type.KeyOf(Type.Object({ x: Type.Number() }), { default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.KeyOf(Type.Object({ x: Type.Number() }), { default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/default/kind.ts b/test/runtime/value/default/kind.ts new file mode 100644 index 000000000..1f8a96fcc --- /dev/null +++ b/test/runtime/value/default/kind.ts @@ -0,0 +1,16 @@ +import { Value } from '@sinclair/typebox/value' +import { Type, Kind } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/Kind', () => { + it('Should use default', () => { + const T = Type.Unsafe({ [Kind]: 'Unknown', default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.Unsafe({ [Kind]: 'Unknown', default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/default/literal.ts b/test/runtime/value/default/literal.ts new file mode 100644 index 000000000..fa01d55b9 --- /dev/null +++ b/test/runtime/value/default/literal.ts @@ -0,0 +1,16 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/Literal', () => { + it('Should use default', () => { + const T = Type.Literal('foo', { default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.Literal('foo', { default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/default/never.ts b/test/runtime/value/default/never.ts new file mode 100644 index 000000000..a39542739 --- /dev/null +++ b/test/runtime/value/default/never.ts @@ -0,0 +1,16 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/Never', () => { + it('Should use default', () => { + const T = Type.Never({ default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.Never({ default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/default/not.ts b/test/runtime/value/default/not.ts new file mode 100644 index 000000000..148756d5b --- /dev/null +++ b/test/runtime/value/default/not.ts @@ -0,0 +1,16 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/Not', () => { + it('Should use default', () => { + const T = Type.Not(Type.String(), { default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.Not(Type.String(), { default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/default/null.ts b/test/runtime/value/default/null.ts new file mode 100644 index 000000000..9246fe893 --- /dev/null +++ b/test/runtime/value/default/null.ts @@ -0,0 +1,16 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/Null', () => { + it('Should use default', () => { + const T = Type.Null({ default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.Null({ default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/default/number.ts b/test/runtime/value/default/number.ts new file mode 100644 index 000000000..6dcaa7035 --- /dev/null +++ b/test/runtime/value/default/number.ts @@ -0,0 +1,16 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/Number', () => { + it('Should use default', () => { + const T = Type.Number({ default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.Number({ default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/default/object.ts b/test/runtime/value/default/object.ts new file mode 100644 index 000000000..ab4dda473 --- /dev/null +++ b/test/runtime/value/default/object.ts @@ -0,0 +1,180 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/Object', () => { + it('Should use default', () => { + const T = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + }, + { default: 1 }, + ) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.Object( + { + x: Type.Number(), + y: Type.Number(), + }, + { default: 1 }, + ) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) + // ---------------------------------------------------------------- + // Construction + // ---------------------------------------------------------------- + it('Should should fully construct object 1', () => { + const T = Type.Object( + { + x: Type.Object( + { + x: Type.Number({ default: 1 }), + y: Type.Number({ default: 2 }), + }, + { default: {} }, + ), + y: Type.Object( + { + x: Type.Number({ default: 3 }), + y: Type.Number({ default: 4 }), + }, + { default: {} }, + ), + }, + { default: {} }, + ) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, { x: { x: 1, y: 2 }, y: { x: 3, y: 4 } }) + }) + it('Should should fully construct object 2', () => { + const T = Type.Object( + { + x: Type.Object( + { + x: Type.Number({ default: 1 }), + y: Type.Number({ default: 2 }), + }, + { default: {} }, + ), + y: Type.Object( + { + x: Type.Number({ default: 3 }), + y: Type.Number({ default: 4 }), + }, + { default: {} }, + ), + }, + { default: {} }, + ) + const R = Value.Default(T, { x: null }) + Assert.IsEqual(R, { x: null, y: { x: 3, y: 4 } }) + }) + it('Should should fully construct object 3', () => { + const T = Type.Object( + { + x: Type.Object( + { + x: Type.Number({ default: 1 }), + y: Type.Number({ default: 2 }), + }, + { default: {} }, + ), + y: Type.Object( + { + x: Type.Number({ default: 3 }), + y: Type.Number({ default: 4 }), + }, + { default: {} }, + ), + }, + { default: {} }, + ) + const R = Value.Default(T, { x: { x: null, y: null } }) + Assert.IsEqual(R, { x: { x: null, y: null }, y: { x: 3, y: 4 } }) + }) + // ---------------------------------------------------------------- + // Properties + // ---------------------------------------------------------------- + it('Should use property defaults 1', () => { + const T = Type.Object( + { + x: Type.Number({ default: 1 }), + y: Type.Number({ default: 2 }), + }, + { default: 1 }, + ) + const R = Value.Default(T, {}) + Assert.IsEqual(R, { x: 1, y: 2 }) + }) + it('Should use property defaults 2', () => { + const T = Type.Object({ + x: Type.Number(), + y: Type.Number(), + }) + const R = Value.Default(T, {}) + Assert.IsEqual(R, {}) + }) + it('Should use property defaults 3', () => { + const T = Type.Object({ + x: Type.Number({ default: 1 }), + y: Type.Number(), + }) + const R = Value.Default(T, {}) + Assert.IsEqual(R, { x: 1 }) + }) + it('Should use property defaults 4', () => { + const T = Type.Object({ + x: Type.Number({ default: 1 }), + y: Type.Number(), + }) + const R = Value.Default(T, { x: 3 }) + Assert.IsEqual(R, { x: 3 }) + }) + // ---------------------------------------------------------------- + // AdditionalProperties + // ---------------------------------------------------------------- + it('Should use additional property defaults 1', () => { + const T = Type.Object( + { + x: Type.Number({ default: 1 }), + y: Type.Number({ default: 2 }), + }, + { + additionalProperties: Type.Number({ default: 3 }), + }, + ) + const R = Value.Default(T, {}) + Assert.IsEqual(R, { x: 1, y: 2 }) + }) + it('Should use additional property defaults 2', () => { + const T = Type.Object( + { + x: Type.Number({ default: 1 }), + y: Type.Number({ default: 2 }), + }, + { + additionalProperties: Type.Number({ default: 3 }), + }, + ) + const R = Value.Default(T, { x: null, y: null, z: undefined }) + Assert.IsEqual(R, { x: null, y: null, z: 3 }) + }) + it('Should use additional property defaults 3', () => { + const T = Type.Object( + { + x: Type.Number({ default: 1 }), + y: Type.Number({ default: 2 }), + }, + { + additionalProperties: Type.Number(), + }, + ) + const R = Value.Default(T, { x: null, y: null, z: undefined }) + Assert.IsEqual(R, { x: null, y: null, z: undefined }) + }) +}) diff --git a/test/runtime/value/default/promise.ts b/test/runtime/value/default/promise.ts new file mode 100644 index 000000000..659e052cb --- /dev/null +++ b/test/runtime/value/default/promise.ts @@ -0,0 +1,16 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/Promise', () => { + it('Should use default', () => { + const T = Type.Any({ default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.Any({ default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/default/record.ts b/test/runtime/value/default/record.ts new file mode 100644 index 000000000..4b300ddc2 --- /dev/null +++ b/test/runtime/value/default/record.ts @@ -0,0 +1,66 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/Record', () => { + it('Should use default', () => { + const T = Type.Record(Type.String(), Type.Number(), { default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.Record(Type.String(), Type.Number(), { default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) + // // ---------------------------------------------------------------- + // // Properties + // // ---------------------------------------------------------------- + it('Should use property defaults 1', () => { + const T = Type.Record(Type.Number(), Type.Number({ default: 1 })) + const R = Value.Default(T, { 0: undefined }) + Assert.IsEqual(R, { 0: 1 }) + }) + it('Should use property defaults 2', () => { + const T = Type.Record(Type.Number(), Type.Number({ default: 1 })) + const R = Value.Default(T, { 0: null }) + Assert.IsEqual(R, { 0: null }) + }) + it('Should use property defaults 3', () => { + const T = Type.Record(Type.Number(), Type.Number({ default: 1 })) + const R = Value.Default(T, { a: undefined }) + Assert.IsEqual(R, { a: undefined }) + }) + it('Should use property defaults 4', () => { + const T = Type.Record(Type.Number(), Type.Number({ default: 1 })) + const R = Value.Default(T, { 0: undefined }) + Assert.IsEqual(R, { 0: 1 }) + }) + it('Should use property defaults 5', () => { + const T = Type.Record(Type.Number(), Type.Number()) + const R = Value.Default(T, { 0: undefined }) + Assert.IsEqual(R, { 0: undefined }) + }) + it('Should use property defaults 6', () => { + const T = Type.Record(Type.Number(), Type.Number({ default: 1 })) + const R = Value.Default(T, {}) + Assert.IsEqual(R, {}) + }) + // ---------------------------------------------------------------- + // Additional Properties + // ---------------------------------------------------------------- + it('Should use additional property defaults 1', () => { + const T = Type.Record(Type.Number(), Type.Number({ default: 1 }), { + additionalProperties: Type.Number({ default: 3 }), + }) + const R = Value.Default(T, { 0: undefined, a: undefined }) + Assert.IsEqual(R, { 0: 1, a: 3 }) + }) + it('Should use additional property defaults 2', () => { + const T = Type.Record(Type.Number(), Type.Number({ default: 1 }), { + additionalProperties: Type.Number(), + }) + const R = Value.Default(T, { 0: undefined, a: undefined }) + Assert.IsEqual(R, { 0: 1, a: undefined }) + }) +}) diff --git a/test/runtime/value/default/recursive.ts b/test/runtime/value/default/recursive.ts new file mode 100644 index 000000000..417727bda --- /dev/null +++ b/test/runtime/value/default/recursive.ts @@ -0,0 +1,58 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +// prettier-ignore +describe('value/default/Recursive', () => { + it('Should use default', () => { + const T = Type.Recursive((This) => Type.Object({ + nodes: Type.Array(This) + }, { default: 1 })) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.Recursive((This) => Type.Object({ + nodes: Type.Array(This) + }, { default: 1 })) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) + // ---------------------------------------------------------------- + // Recursive + // ---------------------------------------------------------------- + it('Should use default recursive values', () => { + const T = Type.Recursive((This) => Type.Object({ + id: Type.String({ default: 1 }), + nodes: Type.Array(This, { default: [] }) // need this + })) + const R = Value.Default(T, { + nodes: [{ + nodes: [{ + nodes: [{ id: null }] + }, { + nodes: [{ id: null }] + }] + }] + }) + Assert.IsEqual(R, { + nodes: [{ + nodes: [{ + nodes: [{ + id: null, + nodes: [] + }], + id: 1 + }, { + nodes: [{ + id: null, + nodes: [] + }], + id: 1 + }], + id: 1 + }], + id: 1 + }) + }) +}) diff --git a/test/runtime/value/default/ref.ts b/test/runtime/value/default/ref.ts new file mode 100644 index 000000000..634059573 --- /dev/null +++ b/test/runtime/value/default/ref.ts @@ -0,0 +1,33 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/Ref', () => { + it('Should use default', () => { + const A = Type.String({ $id: 'A' }) + const T = Type.Ref('A', { default: 1 }) + const R = Value.Default(T, [A], undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const A = Type.String({ $id: 'A' }) + const T = Type.Ref('A', { default: 1 }) + const R = Value.Default(T, [A], null) + Assert.IsEqual(R, null) + }) + // ---------------------------------------------------------------- + // Foreign + // ---------------------------------------------------------------- + it('Should use default on foreign value', () => { + const A = Type.String({ $id: 'A', default: 1 }) + const T = Type.Ref('A') + const R = Value.Default(T, [A], undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value on foreign value', () => { + const A = Type.String({ $id: 'A', default: 1 }) + const T = Type.Ref('A') + const R = Value.Default(T, [A], null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/default/regexp.ts b/test/runtime/value/default/regexp.ts new file mode 100644 index 000000000..9c32c7ab2 --- /dev/null +++ b/test/runtime/value/default/regexp.ts @@ -0,0 +1,16 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/RegExp', () => { + it('Should use default', () => { + const T = Type.RegExp('', { default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.RegExp('', { default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/default/string.ts b/test/runtime/value/default/string.ts new file mode 100644 index 000000000..af833332e --- /dev/null +++ b/test/runtime/value/default/string.ts @@ -0,0 +1,16 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/String', () => { + it('Should use default', () => { + const T = Type.String({ default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.String({ default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/default/symbol.ts b/test/runtime/value/default/symbol.ts new file mode 100644 index 000000000..d593fb7b8 --- /dev/null +++ b/test/runtime/value/default/symbol.ts @@ -0,0 +1,16 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/Symbol', () => { + it('Should use default', () => { + const T = Type.Symbol({ default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.Symbol({ default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/default/template-literal.ts b/test/runtime/value/default/template-literal.ts new file mode 100644 index 000000000..2dd6b7e83 --- /dev/null +++ b/test/runtime/value/default/template-literal.ts @@ -0,0 +1,16 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/TemplateLiteral', () => { + it('Should use default', () => { + const T = Type.TemplateLiteral('hello', { default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.TemplateLiteral('hello', { default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/default/tuple.ts b/test/runtime/value/default/tuple.ts new file mode 100644 index 000000000..c7acdc99b --- /dev/null +++ b/test/runtime/value/default/tuple.ts @@ -0,0 +1,44 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/Tuple', () => { + it('Should use default', () => { + const T = Type.Tuple([Type.Number(), Type.Number()], { default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.Tuple([Type.Number(), Type.Number()], { default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) + // ---------------------------------------------------------------- + // Elements + // ---------------------------------------------------------------- + it('Should use default elements 1', () => { + const T = Type.Tuple([Type.Number({ default: 1 }), Type.Number({ default: 2 })], { default: [] }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) + it('Should use default elements 2', () => { + const T = Type.Tuple([Type.Number({ default: 1 }), Type.Number({ default: 2 })], { default: [] }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, [1, 2]) + }) + it('Should use default elements 3', () => { + const T = Type.Tuple([Type.Number({ default: 1 }), Type.Number({ default: 2 })], { default: [] }) + const R = Value.Default(T, [4, 5, 6]) + Assert.IsEqual(R, [4, 5, 6]) + }) + it('Should use default elements 4', () => { + const T = Type.Tuple([Type.Number({ default: 1 }), Type.Number({ default: 2 })]) + const R = Value.Default(T, [4, 5, 6]) + Assert.IsEqual(R, [4, 5, 6]) + }) + it('Should use default elements 5', () => { + const T = Type.Tuple([Type.Number({ default: 1 }), Type.Number({ default: 2 })]) + const R = Value.Default(T, [4]) + Assert.IsEqual(R, [4, 2]) + }) +}) diff --git a/test/runtime/value/default/uint8array.ts b/test/runtime/value/default/uint8array.ts new file mode 100644 index 000000000..5c3cfa77d --- /dev/null +++ b/test/runtime/value/default/uint8array.ts @@ -0,0 +1,16 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/Uint8Array', () => { + it('Should use default', () => { + const T = Type.Uint8Array({ default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.Uint8Array({ default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/default/undefined.ts b/test/runtime/value/default/undefined.ts new file mode 100644 index 000000000..d622cd1c6 --- /dev/null +++ b/test/runtime/value/default/undefined.ts @@ -0,0 +1,16 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/Undefined', () => { + it('Should use default', () => { + const T = Type.Undefined({ default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.Undefined({ default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/default/union.ts b/test/runtime/value/default/union.ts new file mode 100644 index 000000000..56b572caf --- /dev/null +++ b/test/runtime/value/default/union.ts @@ -0,0 +1,110 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/Union', () => { + it('Should use default', () => { + const T = Type.Union([Type.Number(), Type.String()], { default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.Union([Type.Number(), Type.String()], { default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) + // ---------------------------------------------------------------- + // Interior + // ---------------------------------------------------------------- + it('Should default interior 1', () => { + const T = Type.Union([ + Type.Object({ + x: Type.Number({ default: 1 }), + y: Type.Number({ default: 2 }), + }), + Type.String({ default: 'hello' }), + ]) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) + it('Should default interior 2', () => { + const T = Type.Union([ + Type.Object({ + x: Type.Number({ default: 1 }), + y: Type.Number({ default: 2 }), + }), + Type.String({ default: 'hello' }), + ]) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 'hello') + }) + it('Should default interior 3', () => { + const T = Type.Union([ + Type.Object({ + x: Type.Number({ default: 1 }), + y: Type.Number({ default: 2 }), + }), + Type.String({ default: 'hello' }), + ]) + const R = Value.Default(T, 'world') + Assert.IsEqual(R, 'world') + }) + it('Should default interior 4', () => { + const T = Type.Union([ + Type.Object({ + x: Type.Number({ default: 1 }), + y: Type.Number({ default: 2 }), + }), + Type.String({ default: 'hello' }), + ]) + const R = Value.Default(T, {}) + Assert.IsEqual(R, { x: 1, y: 2 }) + }) + it('Should default interior 5', () => { + const T = Type.Union([ + Type.Object({ + x: Type.Number({ default: 1 }), + y: Type.Number({ default: 2 }), + }), + Type.String({ default: 'hello' }), + ]) + const R = Value.Default(T, { x: 3 }) + Assert.IsEqual(R, { x: 3, y: 2 }) + }) + it('Should default interior 6', () => { + const T = Type.Union([ + Type.Object({ + x: Type.Number({ default: 1 }), + y: Type.Number({ default: 2 }), + }), + Type.String({ default: 'hello' }), + ]) + const R = Value.Default(T, { x: 3, y: 4 }) + Assert.IsEqual(R, { x: 3, y: 4 }) + }) + // ---------------------------------------------------------------- + // Interior Unsafe + // ---------------------------------------------------------------- + it('Should default interior unsafe 1', () => { + const T = Type.Union([ + Type.Object({ + x: Type.Number({ default: 1 }), + y: Type.Number({ default: 2 }), + }), + Type.Unsafe({ default: 'hello' }), + ]) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, undefined) + }) + it('Should default interior unsafe 2', () => { + const T = Type.Union([ + Type.Object({ + x: Type.Number({ default: 1 }), + y: Type.Number({ default: 2 }), + }), + Type.Unsafe({ default: 'hello' }), + ]) + const R = Value.Default(T, 'world') + Assert.IsEqual(R, 'world') + }) +}) diff --git a/test/runtime/value/default/unknown.ts b/test/runtime/value/default/unknown.ts new file mode 100644 index 000000000..97236570d --- /dev/null +++ b/test/runtime/value/default/unknown.ts @@ -0,0 +1,16 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/Unknown', () => { + it('Should use default', () => { + const T = Type.Unknown({ default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.Unknown({ default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/default/void.ts b/test/runtime/value/default/void.ts new file mode 100644 index 000000000..03d03c1e3 --- /dev/null +++ b/test/runtime/value/default/void.ts @@ -0,0 +1,16 @@ +import { Value } from '@sinclair/typebox/value' +import { Type } from '@sinclair/typebox' +import { Assert } from '../../assert/index' + +describe('value/default/Void', () => { + it('Should use default', () => { + const T = Type.Void({ default: 1 }) + const R = Value.Default(T, undefined) + Assert.IsEqual(R, 1) + }) + it('Should use value', () => { + const T = Type.Void({ default: 1 }) + const R = Value.Default(T, null) + Assert.IsEqual(R, null) + }) +}) diff --git a/test/runtime/value/guard/guard.ts b/test/runtime/value/guard/guard.ts index d51511184..ee8d3839f 100644 --- a/test/runtime/value/guard/guard.ts +++ b/test/runtime/value/guard/guard.ts @@ -1,5 +1,5 @@ import { Assert } from '../../assert/index' -import * as ValueGuard from '@sinclair/typebox/value/guard' +import * as ValueGuard from '@sinclair/typebox/value' describe('value/guard/ValueGuard', () => { // ----------------------------------------------------- diff --git a/test/runtime/value/hash/hash.ts b/test/runtime/value/hash/hash.ts index 8d06d509f..aceb06480 100644 --- a/test/runtime/value/hash/hash.ts +++ b/test/runtime/value/hash/hash.ts @@ -1,122 +1,122 @@ -import * as ValueHash from '@sinclair/typebox/value/hash' +import { Hash } from '@sinclair/typebox/value' import { Assert } from '../../assert/index' describe('value/hash/Hash', () => { it('Should hash number', () => { - Assert.IsEqual('bigint', typeof ValueHash.Hash(1)) - const A = ValueHash.Hash(1) - const B = ValueHash.Hash(2) + Assert.IsEqual('bigint', typeof Hash(1)) + const A = Hash(1) + const B = Hash(2) Assert.NotEqual(A, B) }) it('Should hash string', () => { - Assert.IsEqual('bigint', typeof ValueHash.Hash('hello')) - const A = ValueHash.Hash('hello') - const B = ValueHash.Hash('world') + Assert.IsEqual('bigint', typeof Hash('hello')) + const A = Hash('hello') + const B = Hash('world') Assert.NotEqual(A, B) }) it('Should hash boolean', () => { - Assert.IsEqual('bigint', typeof ValueHash.Hash(true)) - Assert.IsEqual('bigint', typeof ValueHash.Hash(false)) - const A = ValueHash.Hash(true) - const B = ValueHash.Hash(false) + Assert.IsEqual('bigint', typeof Hash(true)) + Assert.IsEqual('bigint', typeof Hash(false)) + const A = Hash(true) + const B = Hash(false) Assert.NotEqual(A, B) }) it('Should hash null', () => { - Assert.IsEqual('bigint', typeof ValueHash.Hash(null)) - const A = ValueHash.Hash(null) - const B = ValueHash.Hash(undefined) + Assert.IsEqual('bigint', typeof Hash(null)) + const A = Hash(null) + const B = Hash(undefined) Assert.NotEqual(A, B) }) it('Should hash array', () => { - Assert.IsEqual('bigint', typeof ValueHash.Hash([0, 1, 2, 3])) - const A = ValueHash.Hash([0, 1, 2, 3]) - const B = ValueHash.Hash([0, 2, 2, 3]) + Assert.IsEqual('bigint', typeof Hash([0, 1, 2, 3])) + const A = Hash([0, 1, 2, 3]) + const B = Hash([0, 2, 2, 3]) Assert.NotEqual(A, B) }) it('Should hash object 1', () => { // prettier-ignore - Assert.IsEqual('bigint', typeof ValueHash.Hash({ x: 1, y: 2 })) - const A = ValueHash.Hash({ x: 1, y: 2 }) - const B = ValueHash.Hash({ x: 2, y: 2 }) + Assert.IsEqual('bigint', typeof Hash({ x: 1, y: 2 })) + const A = Hash({ x: 1, y: 2 }) + const B = Hash({ x: 2, y: 2 }) Assert.NotEqual(A, B) }) it('Should hash object 2', () => { - const A = ValueHash.Hash({ x: 1, y: [1, 2] }) - const B = ValueHash.Hash({ x: 1, y: [1, 3] }) + const A = Hash({ x: 1, y: [1, 2] }) + const B = Hash({ x: 1, y: [1, 3] }) Assert.NotEqual(A, B) }) it('Should hash object 3', () => { - const A = ValueHash.Hash({ x: 1, y: undefined }) - const B = ValueHash.Hash({ x: 1 }) + const A = Hash({ x: 1, y: undefined }) + const B = Hash({ x: 1 }) Assert.NotEqual(A, B) }) it('Should hash object 4', () => { - const A = ValueHash.Hash({ x: 1, y: new Uint8Array([0, 1, 2]) }) - const B = ValueHash.Hash({ x: 1, y: [0, 1, 2] }) + const A = Hash({ x: 1, y: new Uint8Array([0, 1, 2]) }) + const B = Hash({ x: 1, y: [0, 1, 2] }) Assert.NotEqual(A, B) }) it('Should hash object 5', () => { - const A = ValueHash.Hash({ x: 1, y: undefined }) - const B = ValueHash.Hash({ x: 2, y: undefined }) + const A = Hash({ x: 1, y: undefined }) + const B = Hash({ x: 2, y: undefined }) Assert.NotEqual(A, B) }) it('Should hash Date', () => { - Assert.IsEqual('bigint', typeof ValueHash.Hash(new Date())) - const A = ValueHash.Hash(new Date(1)) - const B = ValueHash.Hash(new Date(2)) + Assert.IsEqual('bigint', typeof Hash(new Date())) + const A = Hash(new Date(1)) + const B = Hash(new Date(2)) Assert.NotEqual(A, B) }) it('Should hash Uint8Array', () => { - Assert.IsEqual('bigint', typeof ValueHash.Hash(new Uint8Array([0, 1, 2, 3]))) - const A = ValueHash.Hash(new Uint8Array([0, 1, 2, 3])) - const B = ValueHash.Hash(new Uint8Array([0, 2, 2, 3])) + Assert.IsEqual('bigint', typeof Hash(new Uint8Array([0, 1, 2, 3]))) + const A = Hash(new Uint8Array([0, 1, 2, 3])) + const B = Hash(new Uint8Array([0, 2, 2, 3])) Assert.NotEqual(A, B) }) it('Should hash undefined', () => { - Assert.IsEqual('bigint', typeof ValueHash.Hash(undefined)) - const A = ValueHash.Hash(undefined) - const B = ValueHash.Hash(null) + Assert.IsEqual('bigint', typeof Hash(undefined)) + const A = Hash(undefined) + const B = Hash(null) Assert.NotEqual(A, B) }) it('Should hash symbol 1', () => { - Assert.IsEqual('bigint', typeof ValueHash.Hash(Symbol())) - const A = ValueHash.Hash(Symbol(1)) - const B = ValueHash.Hash(Symbol()) + Assert.IsEqual('bigint', typeof Hash(Symbol())) + const A = Hash(Symbol(1)) + const B = Hash(Symbol()) Assert.NotEqual(A, B) }) it('Should hash symbol 2', () => { - Assert.IsEqual('bigint', typeof ValueHash.Hash(Symbol())) - const A = ValueHash.Hash(Symbol(1)) - const B = ValueHash.Hash(Symbol(2)) + Assert.IsEqual('bigint', typeof Hash(Symbol())) + const A = Hash(Symbol(1)) + const B = Hash(Symbol(2)) Assert.NotEqual(A, B) }) it('Should hash symbol 2', () => { - Assert.IsEqual('bigint', typeof ValueHash.Hash(Symbol())) - const A = ValueHash.Hash(Symbol(1)) - const B = ValueHash.Hash(Symbol(1)) + Assert.IsEqual('bigint', typeof Hash(Symbol())) + const A = Hash(Symbol(1)) + const B = Hash(Symbol(1)) Assert.IsEqual(A, B) }) it('Should hash bigint 1', () => { - Assert.IsEqual('bigint', typeof ValueHash.Hash(BigInt(1))) - const A = ValueHash.Hash(BigInt(1)) - const B = ValueHash.Hash(BigInt(2)) + Assert.IsEqual('bigint', typeof Hash(BigInt(1))) + const A = Hash(BigInt(1)) + const B = Hash(BigInt(2)) Assert.NotEqual(A, B) }) it('Should hash bigint 2', () => { - Assert.IsEqual('bigint', typeof ValueHash.Hash(BigInt(1))) - const A = ValueHash.Hash(BigInt(1)) - const B = ValueHash.Hash(BigInt(1)) + Assert.IsEqual('bigint', typeof Hash(BigInt(1))) + const A = Hash(BigInt(1)) + const B = Hash(BigInt(1)) Assert.IsEqual(A, B) }) // ---------------------------------------------------------------- // Unicode // ---------------------------------------------------------------- it('Should hash unicode 1 (retain single byte hash)', () => { - const hash = ValueHash.Hash('a') + const hash = Hash('a') Assert.IsEqual(hash, 586962220959696054n) }) it('Should hash unicode 2', () => { - const hash = ValueHash.Hash('안녕 세계') + const hash = Hash('안녕 세계') Assert.IsEqual(hash, 11219208047802711777n) }) }) diff --git a/test/runtime/value/index.ts b/test/runtime/value/index.ts index de126d75d..4e6ce996e 100644 --- a/test/runtime/value/index.ts +++ b/test/runtime/value/index.ts @@ -1,8 +1,10 @@ import './cast' import './check' +import './clean' import './clone' import './convert' import './create' +import './default' import './delta' import './equal' import './guard' diff --git a/test/runtime/value/transform/_encoder.ts b/test/runtime/value/transform/_encoder.ts index 06d82410b..22a2b1d1d 100644 --- a/test/runtime/value/transform/_encoder.ts +++ b/test/runtime/value/transform/_encoder.ts @@ -1,4 +1,4 @@ -import { IsAsyncIterator, IsIterator, IsFunction, IsSymbol, IsDate } from '@sinclair/typebox/value/guard' +import { IsAsyncIterator, IsIterator, IsFunction, IsSymbol, IsDate } from '@sinclair/typebox/value' import { TSchema, StaticDecode, StaticEncode } from '@sinclair/typebox' import { TypeCompiler } from '@sinclair/typebox/compiler' import { Value } from '@sinclair/typebox/value' diff --git a/test/runtime/value/transform/union.ts b/test/runtime/value/transform/union.ts index 93eeff81c..43152417f 100644 --- a/test/runtime/value/transform/union.ts +++ b/test/runtime/value/transform/union.ts @@ -1,7 +1,6 @@ import * as Encoder from './_encoder' import { Assert } from '../../assert' -import { Value } from '@sinclair/typebox/value' -import { Type, TSchema } from '@sinclair/typebox' +import { Type } from '@sinclair/typebox' describe('value/transform/Union', () => { // -------------------------------------------------------- @@ -199,4 +198,44 @@ describe('value/transform/Union', () => { it('Should throw on interior union encode', () => { Assert.Throws(() => Encoder.Encode(T52, 1)) }) + // prettier-ignore + { // https://github.com/sinclairzx81/typebox/issues/676 + // interior-type + const S = Type.Transform(Type.String()) + .Decode((value: string) => new globalThis.Date(value)) + .Encode((value: Date) => value.toISOString()) + // union-type + const U = Type.Union([ + Type.Object({ date: S }), + Type.Number() + ]) + // expect date on decode + const T1 = Type.Transform(U) + .Decode((value) => { + Assert.IsTypeOf(value, 'object') + Assert.HasProperty(value, 'date') + Assert.IsInstanceOf(value.date, globalThis.Date); + return value + }) + .Encode((value) => value) + // expect number on decode + const T2 = Type.Transform(U) + .Decode((value) => { + Assert.IsTypeOf(value, 'number') + return value + }) + .Encode((value) => value) + + it('Should decode interior union 1', () => { + const R = Encoder.Decode(T1, { date: new globalThis.Date().toISOString() }) + Assert.IsTypeOf(R, 'object') + Assert.HasProperty(R, 'date') + Assert.IsInstanceOf(R.date, globalThis.Date); + }) + it('Should decode interior union 2', () => { + const R = Encoder.Decode(T2, 123) + Assert.IsTypeOf(R, 'number') + Assert.IsEqual(R, 123) + }) + } }) diff --git a/test/static/assert.ts b/test/static/assert.ts index 9a10c8c07..862731dbf 100644 --- a/test/static/assert.ts +++ b/test/static/assert.ts @@ -27,22 +27,24 @@ export type CircularHelper = [T] extends U ? T : Expected // See https://github.com/Microsoft/TypeScript/issues/27024 export type ConstrainEqual = (() => V extends T ? 1 : 2) extends () => V extends U ? 1 : 2 ? T : Expected export type ConstraintMutuallyExtend = CircularHelper + +// Circular Error on TS 5.4.0 // If U is never, there's nothing we can do -export type ComplexConstraint = If< - // If U is any, we can't use Expect or it would satisfy the constraint - And>, IsAny>, - never, - If< - Or< - // If they are both any we are happy - And, IsAny>, - // If T extends U, but not because it's any, we are happy - And, Not>> - >, - T, - Expected - > -> +// export type ComplexConstraint = If< +// // If U is any, we can't use Expect or it would satisfy the constraint +// And>, IsAny>, +// never, +// If< +// Or< +// // If they are both any we are happy +// And, IsAny>, +// // If T extends U, but not because it's any, we are happy +// And, Not>> +// >, +// T, +// Expected +// > +// > // ------------------------------------------------------------------ // Expect // ------------------------------------------------------------------ @@ -50,9 +52,12 @@ export type ExpectResult = If< IsNever>, { ToStaticNever(): void }, { - ToStatic, U>>(): void - ToStaticDecode, U>>(): void - ToStaticEncode, U>>(): void + ToStatic, U>>(): void + ToStaticDecode, U>>(): void + ToStaticEncode, U>>(): void + // ToStatic>(): void + // ToStaticDecode>(): void + // ToStaticEncode>(): void } > export function Expect(schema: T) { diff --git a/test/static/const.ts b/test/static/const.ts new file mode 100644 index 000000000..d81d0fed7 --- /dev/null +++ b/test/static/const.ts @@ -0,0 +1,41 @@ +import { Expect } from './assert' +import { Type } from '@sinclair/typebox' + +// ------------------------------------------------------------------ +// Identity Types +// ------------------------------------------------------------------ +// prettier-ignore +Expect(Type.Const(undefined)).ToStatic() +// prettier-ignore +Expect(Type.Const(null)).ToStatic() +// prettier-ignore +Expect(Type.Const(Symbol())).ToStatic() +// prettier-ignore +Expect(Type.Const(1 as const)).ToStatic<1>() +// prettier-ignore +Expect(Type.Const('hello' as const)).ToStatic<'hello'>() +// prettier-ignore +Expect(Type.Const(true as const)).ToStatic() + +// ------------------------------------------------------------------ +// Complex Types +// ------------------------------------------------------------------ +// prettier-ignore +Expect(Type.Const(100n)).ToStatic() +// prettier-ignore +Expect(Type.Const(new Date())).ToStatic() +// prettier-ignore +Expect(Type.Const(new Uint8Array())).ToStatic() +// prettier-ignore +Expect(Type.Const(function () {})).ToStatic<() => unknown>() +// prettier-ignore +Expect(Type.Const((function *(): any {})())).ToStatic() +// prettier-ignore +Expect(Type.Const((async function *(): any {})())).ToStatic() +// todo: remove when dropping TS 4.0 +// prettier-ignore +Expect(Type.Const({ x: 1, y: { z: 2 } })).ToStatic<{ readonly x: number, readonly y: { readonly z: number }}>() +// prettier-ignore +Expect(Type.Const({ x: 1, y: { z: 2 } } as const)).ToStatic<{ readonly x: 1, readonly y: { readonly z: 2 }}>() +// prettier-ignore +Expect(Type.Const([1, 2, 3] as const)).ToStatic<[1, 2, 3]>() diff --git a/test/static/deref.ts b/test/static/deref.ts new file mode 100644 index 000000000..4df0737ef --- /dev/null +++ b/test/static/deref.ts @@ -0,0 +1,56 @@ +import { Expect } from './assert' +import { Type, TRef, TObject, TNumber } from '@sinclair/typebox' + +// prettier-ignore +const Vector: TObject<{ + x: TNumber; + y: TNumber; +}> = Type.Object({ + x: Type.Number(), + y: Type.Number(), +}, { $id: 'Vector' }) + +// prettier-ignore +const VectorRef: TRef> = Type.Ref(Vector) + +// prettier-ignore +const Vertex: TObject<{ + position: TRef>; + texcoord: TRef>; +}> = Type.Object({ + position: VectorRef, + texcoord: VectorRef, +}) + +// prettier-ignore +const VertexDeref: TObject<{ + position: TObject<{ + x: TNumber; + y: TNumber; + }>; + texcoord: TObject<{ + x: TNumber; + y: TNumber; + }>; +}> = Type.Deref(Vertex, [Vector]) + +// prettier-ignore +Expect(VertexDeref).ToStatic<{ + position: { + x: number; + y: number; + }; + texcoord: { + x: number; + y: number; + }; +}> diff --git a/test/static/index.ts b/test/static/index.ts index 911f5ec19..264e67ae3 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' diff --git a/test/static/indexed.ts b/test/static/indexed.ts index a33af5130..fe5875b7b 100644 --- a/test/static/indexed.ts +++ b/test/static/indexed.ts @@ -54,13 +54,13 @@ import { Type, Static } from '@sinclair/typebox' const A = Type.Object({}) const R = Type.Index(A, Type.BigInt()) // Support Overload type O = Static - Expect(R).ToStatic() + Expect(R).ToStaticNever() } { const A = Type.Array(Type.Number()) const R = Type.Index(A, Type.BigInt()) // Support Overload type O = Static - Expect(R).ToStatic() + Expect(R).ToStaticNever() } // ------------------------------------------------------------------ // Intersections @@ -174,6 +174,9 @@ import { Type, Static } from '@sinclair/typebox' const D = Type.Object({ x: Type.String() }) const I = Type.Intersect([Type.Intersect([A, B]), Type.Intersect([C, D])]) const R = Type.Index(I, ['x', 'y']) + + // TUnion<[TIntersect<[TIntersect<[TString, TNumber]>, TIntersect<[TString, TString]>]>, TIntersect<[TIntersect<[TLiteral<...>, TNumber]>, TNumber]>]> + // TUnion<[TUnion<[TString, TNumber, TString, TString]>, TUnion<[TLiteral<1>, TNumber, TNumber]>]> type O = Static Expect(R).ToStatic<1>() } @@ -191,6 +194,10 @@ import { Type, Static } from '@sinclair/typebox' const D = Type.Object({ x: Type.String() }) const I = Type.Intersect([Type.Union([A, B]), Type.Intersect([C, D])]) const R = Type.Index(I, ['x', 'y']) + + // TUnion<[TIntersect<[TUnion<[TString, TNumber]>, TIntersect<[TString, TIntersect<[TString, TIntersect<[]>]>, TIntersect<[]>]>]>, TIntersect<...>]> + // TUnion<[TIntersect<[TUnion<[TString, TNumber]>, TIntersect<[TString, TString]>]>, TIntersect<[TUnion<[TLiteral<1>, TNumber]>, TNumber]>]> + // TUnion<[TIntersect<[TUnion<[TString, TNumber]>, TString, TString]>, TIntersect<[TUnion<[TLiteral<1>, TNumber]>, TNumber]>]> type O = Static Expect(R).ToStatic() } @@ -207,7 +214,7 @@ import { Type, Static } from '@sinclair/typebox' const C = Type.Object({ x: Type.Literal('C'), y: Type.Number() }) const D = Type.Object({ x: Type.Literal('D') }) const I = Type.Union([A, B, C, D]) - const R = Type.Index(I, Type.Union([Type.Literal('x')])) + const R = Type.Index(I, ['x']) type O = Static Expect(R).ToStatic<'A' | 'B' | 'C' | 'D'>() } @@ -243,7 +250,7 @@ import { Type, Static } from '@sinclair/typebox' } { const T = Type.Object({ - 0: Type.Number(), + '0': Type.Number(), '1': Type.String(), }) const R = Type.Index(T, Type.KeyOf(T)) @@ -257,3 +264,13 @@ import { Type, Static } from '@sinclair/typebox' }) Expect(R).ToStatic<{ x: number | string }>() } +{ + const T = Type.Array(Type.String()) + const I = Type.Index(T, Type.Number()) + Expect(I).ToStatic() +} +{ + const T = Type.Array(Type.String()) + const I = Type.Index(T, ['[number]']) + Expect(I).ToStatic() +} diff --git a/test/static/record.ts b/test/static/record.ts index e21356595..e284ce053 100644 --- a/test/static/record.ts +++ b/test/static/record.ts @@ -20,7 +20,6 @@ import { Type, Static } from '@sinclair/typebox' const K = Type.Number() const T = Type.Record(K, Type.Number()) type T = Static - Expect(T).ToStatic>() Expect(T).ToStatic>() } { @@ -174,5 +173,5 @@ import { Type, Static } from '@sinclair/typebox' // expect T to support named properties enum E {} const T = Type.Record(Type.Enum(E), Type.Number()) - Expect(T).ToStatic<{}> + Expect(T).ToStatic<{ [x: string]: number }> } diff --git a/test/static/transform.ts b/test/static/transform.ts index 917628a67..1f4176289 100644 --- a/test/static/transform.ts +++ b/test/static/transform.ts @@ -215,12 +215,12 @@ import { Expect } from './assert' // should decode within generic function context // https://github.com/sinclairzx81/typebox/issues/554 // prettier-ignore - const ArrayOrSingle = (schema: T) => - Type.Transform(Type.Union([schema, Type.Array(schema)])) - .Decode((value) => (Array.isArray(value) ? value : [value])) - .Encode((value) => (value.length === 1 ? value[0] : value) as Static[]); - const T = ArrayOrSingle(Type.String()) - Expect(T).ToStaticDecode() + // const ArrayOrSingle = (schema: T) => + // Type.Transform(Type.Union([schema, Type.Array(schema)])[0]) + // .Decode((value) => (Array.isArray(value) ? value : [value])) + // .Encode((value) => (value.length === 1 ? value[0] : value) as Static[]); + // const T = ArrayOrSingle(Type.String()) + // Expect(T).ToStaticDecode() } { // should correctly decode record keys diff --git a/tsconfig.json b/tsconfig.json index ec148af39..6a47a31ec 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,63 +1,17 @@ { "compilerOptions": { "strict": true, - "target": "ES2020", - "module": "CommonJS", - "moduleResolution": "node", - "declaration": true, + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "Node", "baseUrl": ".", "paths": { - "@sinclair/typebox/compiler": [ - "src/compiler/index.ts" - ], - "@sinclair/typebox/errors": [ - "src/errors/index.ts" - ], - "@sinclair/typebox/system": [ - "src/system/index.ts" - ], - "@sinclair/typebox/value/cast": [ - "src/value/cast.ts" - ], - "@sinclair/typebox/value/check": [ - "src/value/check.ts" - ], - "@sinclair/typebox/value/clone": [ - "src/value/clone.ts" - ], - "@sinclair/typebox/value/convert": [ - "src/value/convert.ts" - ], - "@sinclair/typebox/value/create": [ - "src/value/create.ts" - ], - "@sinclair/typebox/value/delta": [ - "src/value/delta.ts" - ], - "@sinclair/typebox/value/equal": [ - "src/value/equal.ts" - ], - "@sinclair/typebox/value/guard": [ - "src/value/guard.ts" - ], - "@sinclair/typebox/value/hash": [ - "src/value/hash.ts" - ], - "@sinclair/typebox/value/mutate": [ - "src/value/mutate.ts" - ], - "@sinclair/typebox/value/pointer": [ - "src/value/pointer.ts" - ], - "@sinclair/typebox/value/transform": [ - "src/value/transform/index.ts" - ], - "@sinclair/typebox/value": [ - "src/value/index.ts" - ], - "@sinclair/typebox": [ - "src/typebox.ts" - ], + "@sinclair/typebox/compiler": ["src/compiler/index.ts"], + "@sinclair/typebox/errors": ["src/errors/index.ts"], + "@sinclair/typebox/system": ["src/system/index.ts"], + "@sinclair/typebox/type": ["src/type/index.ts"], + "@sinclair/typebox/value": ["src/value/index.ts"], + "@sinclair/typebox": ["src/index.ts"], } } } \ No newline at end of file