mirror of
https://github.com/zoriya/typebox.git
synced 2025-12-05 22:36:12 +00:00
196
changelog/0.34.0.md
Normal file
196
changelog/0.34.0.md
Normal file
@@ -0,0 +1,196 @@
|
||||
## [0.34.0](https://www.npmjs.com/package/@sinclair/typebox/v/0.34.0)
|
||||
|
||||
## Overview
|
||||
|
||||
Revision 0.34.0 represents a significant milestone for the TypeBox project. This update changes how TypeBox manages type references (Ref) and introduces a new Module type to support mutual recursion and self-referencing types. Additionally, it includes a new submodule for parsing TypeScript syntax directly into TypeBox types.
|
||||
|
||||
Please note that this release includes breaking changes to Ref and some deprecations. These updates require a minor semver revision.
|
||||
|
||||
## Contents
|
||||
|
||||
- [Enhancements](#Enhancements)
|
||||
- [Module Types](#Module-Types)
|
||||
- [Syntax Types](#Syntax-Types)
|
||||
- [Breaking Changes](#Breaking-Changes)
|
||||
- [Ref](#Ref)
|
||||
- [Deref](#Deref)
|
||||
- [Strict](#Strict)
|
||||
|
||||
|
||||
<a name="Enhancements"></a>
|
||||
|
||||
## Enhancements
|
||||
|
||||
Below are the enhancements introduced in Version 0.34.0.
|
||||
|
||||
<a name="Module-Type"></a>
|
||||
|
||||
### Module Types
|
||||
|
||||
Revision 0.34.0 introduces a new type, called Module. Modules are represented as JSON Schema $def schematics, specifically designed to support both mutual and self-recursive types. This addition resolves a longstanding issue in TypeBox, where complex recursive structures often encountered "definition order" problems, making certain recursive structures difficult to represent cleanly. With Modules, you can now define schematics within a Module context, allowing them to be referenced within the type system through a separate inference operation.
|
||||
|
||||
```typescript
|
||||
// The following creates a circular recursive type.
|
||||
|
||||
const Module = Type.Module({
|
||||
A: Type.Object({
|
||||
b: Type.Ref('B') // Ref B:
|
||||
}),
|
||||
B: Type.Object({
|
||||
c: Type.Ref('C') // Ref C:
|
||||
}),
|
||||
C: Type.Object({
|
||||
a: Type.Ref('A') // Ref A:
|
||||
}),
|
||||
})
|
||||
|
||||
// Module types must be imported before use.
|
||||
|
||||
const A = Module.Import('A') // const A: TImport<{...}, 'A'>
|
||||
|
||||
type A = Static<typeof A> // type A = {
|
||||
// b: {
|
||||
// c: {
|
||||
// a: {
|
||||
// b: ...
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
```
|
||||
|
||||
<a name="Syntax-Types"></a>
|
||||
|
||||
### Syntax Types
|
||||
|
||||
|
||||
Revision 0.34.0 introduces a new submodule for parsing TypeScript syntax directly into TypeBox types, implemented both at runtime and within the type system. This feature was made possible through the development of a separate project, [ParseBox](https://github.com/sinclairzx81/parsebox) (MIT-licensed), which provides a symmetric runtime and type-level parsing infrastructure.
|
||||
|
||||
As of 0.34.0, Syntax Types are available as an opt-in feature, with the parsing infrastructure adding approximately 10kb (minified) to the existing type builder. With further optimizations, this feature may be elevated to a top-level import in future updates to minimize bundling size.
|
||||
|
||||
To use Syntax Types, import them from the `@sinclair/typebox/syntax` path.
|
||||
|
||||
```typescript
|
||||
import { Parse } from '@sinclair/typebox/syntax'
|
||||
|
||||
// All primitive types are supported
|
||||
|
||||
const A = Parse('string') // const A: TString
|
||||
const B = Parse('number') // const B: TNumber
|
||||
const C = Parse('boolean') // const C: TBoolean
|
||||
|
||||
// ... Multiline parsing is supported (but comments are not)
|
||||
|
||||
const T = Parse(`{
|
||||
x: number
|
||||
y: number
|
||||
z: number
|
||||
}`)
|
||||
|
||||
|
||||
// ... Parametertized parsing is supported
|
||||
const O = Parse({ T }, `T & { w: number }`) // const O: TIntersect<[
|
||||
// TObject<{
|
||||
// x: TNumber,
|
||||
// y: TNumber,
|
||||
// z: TNumber,
|
||||
// }>,
|
||||
// TObject<{
|
||||
// w: TNumber
|
||||
// }>
|
||||
// ]>
|
||||
|
||||
// ... Module parsing is also supported.
|
||||
|
||||
const Math = Parse(`module Math {
|
||||
export interface X {
|
||||
x: number
|
||||
}
|
||||
export interface Y {
|
||||
y: number
|
||||
}
|
||||
export interface Z {
|
||||
z: number
|
||||
}
|
||||
export interface Vector extends X, Y, Z {
|
||||
type: 'Vector'
|
||||
}
|
||||
}`)
|
||||
|
||||
const Vector = Math.Import('Vector')
|
||||
```
|
||||
|
||||
Runtime parsing performance should be quite good; however, static parsing performance could be improved. TypeScript will invoke the parser for each property accessed at design time. Ongoing efforts within the ParseBox project aim to optimize string parsing in TypeScript, with additional research underway into type-level caching strategies within the TypeScript compiler. Additional optimizations will be explored over the course of 0.34.x.
|
||||
|
||||
<a name="Breaking-Changes"></a>
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
The following are the breaking changes in Revision 0.34.0.
|
||||
|
||||
<a name="Ref"></a>
|
||||
|
||||
### Ref
|
||||
|
||||
Revision 0.34.0 introduces a breaking change to Ref, modifying its signature to accept only constant string values. Previously, Ref could accept an existing TypeBox type, provided it had an $id assigned.
|
||||
|
||||
```typescript
|
||||
|
||||
// Revision 0.33.0
|
||||
|
||||
const T = Type.String({ $id: 'T' })
|
||||
|
||||
const R = Type.Ref(T)
|
||||
|
||||
type R = Static<typeof R> // type R = string
|
||||
|
||||
// Revision 0.34.0
|
||||
|
||||
const T = Type.String({ $id: 'T' })
|
||||
|
||||
const R = Type.Ref('T')
|
||||
|
||||
type R = Static<typeof R> // type R = unknown
|
||||
|
||||
```
|
||||
|
||||
In Revision 0.34.0, the inferred type for Ref is now unknown. Implementations using the previous version of Ref can switch to Unsafe to type the reference to the target value.
|
||||
|
||||
```typescript
|
||||
// Revision 0.34.0
|
||||
|
||||
const T = Type.String({ $id: 'T' })
|
||||
|
||||
const R = Type.Unsafe<Static<typeof T>>(Type.Ref('T'))
|
||||
|
||||
type R = Static<typeof R> // type R = string
|
||||
```
|
||||
|
||||
<a name="Deref"></a>
|
||||
|
||||
### Deref
|
||||
|
||||
Revision 0.34.0 removes the Deref type, which was previously used to dereference schematics. Since the Ref signature has changed from TSchema to string, there is no longer a way to resolve reference types accurately. TypeBox may provide a prototype example of this type upon request.
|
||||
|
||||
<a name="Strict"></a>
|
||||
|
||||
### Strict
|
||||
|
||||
Revision 0.34.0 removes the Strict type from the Type Builder, which was deprecated in version 0.33.8. This type was introduced several years ago in response to a change in Ajv that prohibited unknown keywords. At that time, TypeBox used string property keys for `kind` and `modifier`, which required either Ajv configuration or the use of Strict. These properties have since been updated to Symbol properties, resolving the issues with Ajv. However, the Strict type remained due to some use in ecosystem projects, which has since reduced.
|
||||
|
||||
For those who still need Strict, the recommended approach is to use the JSON stringify/parse method outlined in the deprecation notice.
|
||||
|
||||
```typescript
|
||||
/**
|
||||
* @deprecated `[Json]` Omits compositing symbols from this schema. It is recommended
|
||||
* to use the JSON parse/stringify to remove compositing symbols if needed. This
|
||||
* is how Strict works internally.
|
||||
*
|
||||
* ```typescript
|
||||
* JSON.parse(JSON.stringify(Type.String()))
|
||||
* ```
|
||||
*/
|
||||
export function Strict<T extends TSchema>(schema: T): TStrict<T> {
|
||||
return JSON.parse(JSON.stringify(schema))
|
||||
}
|
||||
```
|
||||
@@ -27,7 +27,6 @@ THE SOFTWARE.
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
export * from './from-schema'
|
||||
export * from './module'
|
||||
export * from './partial-deep'
|
||||
export * from './union-enum'
|
||||
export * from './union-oneof'
|
||||
|
||||
@@ -1,166 +0,0 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
@sinclair/typebox/prototypes
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2024 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
import * as Types from '@sinclair/typebox'
|
||||
import { Check } from '@sinclair/typebox/value'
|
||||
// ------------------------------------------------------------------
|
||||
// Infer
|
||||
// ------------------------------------------------------------------
|
||||
// prettier-ignore
|
||||
export type InferImport<Properties extends TModuleProperties, Key extends keyof Properties, Module extends TModuleProperties> = (
|
||||
Infer<Properties[Key], Module>
|
||||
)
|
||||
// prettier-ignore
|
||||
export type InferModuleRef<Ref extends string, Module extends TModuleProperties> = (
|
||||
Ref extends keyof Module ? Infer<Module[Ref], Module> : never
|
||||
)
|
||||
// prettier-ignore
|
||||
export type InferObject<Properties extends Types.TProperties, Module extends TModuleProperties> = {
|
||||
[K in keyof Properties]: Infer<Properties[K], Module>
|
||||
} & {}
|
||||
// prettier-ignore
|
||||
export type InferConstructor<Parameters extends Types.TSchema[], InstanceType extends Types.TSchema, Module extends TModuleProperties> = Types.Ensure<
|
||||
new (...args: InferTuple<Parameters, Module>) => Infer<InstanceType, Module>
|
||||
>
|
||||
// prettier-ignore
|
||||
export type InferFunction<Parameters extends Types.TSchema[], ReturnType extends Types.TSchema, Module extends TModuleProperties> = Types.Ensure<
|
||||
(...args: InferTuple<Parameters, Module>) => Infer<ReturnType, Module>
|
||||
>
|
||||
// prettier-ignore
|
||||
export type InferTuple<Types extends Types.TSchema[], Module extends TModuleProperties, Result extends unknown[] = []> = (
|
||||
Types extends [infer L extends Types.TSchema, ...infer R extends Types.TSchema[]]
|
||||
? InferTuple<R, Module, [...Result, Infer<L, Module>]>
|
||||
: Result
|
||||
)
|
||||
// prettier-ignore
|
||||
export type InferIntersect<Types extends Types.TSchema[], Module extends TModuleProperties, Result extends unknown = unknown> = (
|
||||
Types extends [infer L extends Types.TSchema, ...infer R extends Types.TSchema[]]
|
||||
? InferIntersect<R, Module, Result & Infer<L, Module>>
|
||||
: Result
|
||||
)
|
||||
// prettier-ignore
|
||||
export type InferUnion<Types extends Types.TSchema[], Module extends TModuleProperties, Result extends unknown = never> = (
|
||||
Types extends [infer L extends Types.TSchema, ...infer R extends Types.TSchema[]]
|
||||
? InferUnion<R, Module, Result | Infer<L, Module>>
|
||||
: Result
|
||||
)
|
||||
// prettier-ignore
|
||||
export type InferArray<Type extends Types.TSchema, Module extends TModuleProperties> = (
|
||||
Types.Ensure<Array<Infer<Type, Module>>>
|
||||
)
|
||||
// prettier-ignore
|
||||
export type InferAsyncIterator<Type extends Types.TSchema, Module extends TModuleProperties> = (
|
||||
Types.Ensure<AsyncIterableIterator<Infer<Type, Module>>>
|
||||
)
|
||||
// prettier-ignore
|
||||
export type InferIterator<Type extends Types.TSchema, Module extends TModuleProperties> = (
|
||||
Types.Ensure<IterableIterator<Infer<Type, Module>>>
|
||||
)
|
||||
// prettier-ignore
|
||||
type Infer<Type extends Types.TSchema, Module extends TModuleProperties = {}> = (
|
||||
Type extends TImport<infer S extends TModuleProperties, infer K extends PropertyKey> ? InferImport<S, K, S> :
|
||||
Type extends TModuleRef<infer S extends string> ? InferModuleRef<S, Module> :
|
||||
Type extends Types.TObject<infer S extends Types.TProperties> ? InferObject<S, Module> :
|
||||
Type extends Types.TConstructor<infer S extends Types.TSchema[], infer R extends Types.TSchema> ? InferConstructor<S, R, Module> :
|
||||
Type extends Types.TFunction<infer S extends Types.TSchema[], infer R extends Types.TSchema> ? InferFunction<S, R, Module> :
|
||||
Type extends Types.TTuple<infer S extends Types.TSchema[]> ? InferTuple<S, Module> :
|
||||
Type extends Types.TIntersect<infer S extends Types.TSchema[]> ? InferIntersect<S, Module> :
|
||||
Type extends Types.TUnion<infer S extends Types.TSchema[]> ? InferUnion<S, Module> :
|
||||
Type extends Types.TArray<infer S extends Types.TSchema> ? InferArray<S, Module> :
|
||||
Type extends Types.TAsyncIterator<infer S extends Types.TSchema> ? InferAsyncIterator<S, Module> :
|
||||
Type extends Types.TIterator<infer S extends Types.TSchema> ? InferIterator<S, Module> :
|
||||
Type extends Types.TTemplateLiteral<infer S extends Types.TTemplateLiteralKind[]> ? Types.Static<Types.TTemplateLiteral<S>> :
|
||||
Type extends Types.TLiteral<infer S extends Types.TLiteralValue> ? S :
|
||||
Type extends Types.TAny ? any :
|
||||
Type extends Types.TBigInt ? bigint :
|
||||
Type extends Types.TBoolean ? boolean :
|
||||
Type extends Types.TDate ? Date :
|
||||
Type extends Types.TInteger ? number :
|
||||
Type extends Types.TNever ? never :
|
||||
Type extends Types.TNumber ? number :
|
||||
Type extends Types.TRegExp ? string :
|
||||
Type extends Types.TString ? string :
|
||||
Type extends Types.TSymbol ? symbol :
|
||||
Type extends Types.TNull ? null :
|
||||
Type extends Types.TUint8Array ? Uint8Array :
|
||||
Type extends Types.TUndefined ? undefined :
|
||||
Type extends Types.TUnknown ? unknown :
|
||||
Type extends Types.TVoid ? void :
|
||||
never
|
||||
)
|
||||
// ------------------------------------------------------------------
|
||||
// ModuleRef
|
||||
// ------------------------------------------------------------------
|
||||
// prettier-ignore
|
||||
export interface TModuleRef<Ref extends string> extends Types.TSchema {
|
||||
[Types.Kind]: 'ModuleRef'
|
||||
$ref: Ref
|
||||
}
|
||||
// prettier-ignore
|
||||
export function ModuleRef<Ref extends string>($ref: Ref): TModuleRef<Ref> {
|
||||
return Types.Type.Ref($ref) as never
|
||||
}
|
||||
// ------------------------------------------------------------------
|
||||
// Import
|
||||
// ------------------------------------------------------------------
|
||||
// prettier-ignore
|
||||
Types.TypeRegistry.Set('Import', (module: TImport, value: unknown) => {
|
||||
const keys = Object.getOwnPropertyNames(module.$defs)
|
||||
const references = keys.map(key => module.$defs[key as never]) as Types.TSchema[]
|
||||
const schema = module.$defs[module.$ref]
|
||||
return Check(schema, references, value)
|
||||
})
|
||||
// prettier-ignore
|
||||
export type TModuleProperties = Record<PropertyKey, Types.TSchema>
|
||||
|
||||
// prettier-ignore
|
||||
export interface TImport<Definitions extends TModuleProperties = {}, Key extends keyof Definitions = keyof Definitions> extends Types.TSchema {
|
||||
[Types.Kind]: 'Import'
|
||||
static: InferImport<Definitions, Key, Definitions>
|
||||
$defs: Definitions
|
||||
$ref: Key
|
||||
}
|
||||
// ------------------------------------------------------------------
|
||||
// Module
|
||||
// ------------------------------------------------------------------
|
||||
// prettier-ignore
|
||||
export class ModuleInstance<Properties extends TModuleProperties> {
|
||||
constructor(private readonly properties: Properties) {}
|
||||
public Import<Key extends keyof Properties>(key: Key): TImport<Properties, Key> {
|
||||
const $defs = globalThis.Object.getOwnPropertyNames(this.properties).reduce((Result, Key) => (
|
||||
{ ...Result, [Key]: { ...this.properties[Key], $id: Key }}
|
||||
), {})
|
||||
return { [Types.Kind]: 'Import', $defs, $ref: key } as never
|
||||
}
|
||||
}
|
||||
export function Module<Properties extends TModuleProperties>(properties: Properties): ModuleInstance<Properties> {
|
||||
return new ModuleInstance(properties)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -2,46 +2,6 @@
|
||||
|
||||
TypeBox prototypes are a set of types that are either under consideration for inclusion into the library, or have been requested by users but cannot be added to the library either due to complexity, using schematics that fall outside the supported TypeBox or should be expressed by users via advanced type composition.
|
||||
|
||||
## Module, ModuleRef and Import
|
||||
|
||||
The Module type as a candidate referencing system for TypeBox. Modules enable deferred cross type referencing and support mutual recursive inference. Module types must be instanced via `M.Import(...)` which constructs a `$def` schematic containing each definition required to validate, and a self referential `$ref` to one of the type being imported.
|
||||
|
||||
```typescript
|
||||
import { Module, ModuleRef } from './prototypes'
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Module, ModuleRef
|
||||
// ------------------------------------------------------------------
|
||||
const Math = Module({
|
||||
Vector2: Type.Object({
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
}),
|
||||
Vector3: Type.Object({
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
z: Type.Number()
|
||||
}),
|
||||
Vertex: Type.Object({
|
||||
position: ModuleRef('Vector3'),
|
||||
normal: ModuleRef('Vector3'),
|
||||
texcoord: ModuleRef('Vector2')
|
||||
}),
|
||||
Geometry: Type.Object({
|
||||
vertices: Type.Array(ModuleRef('Vertex')),
|
||||
indices: Type.Array(Type.Integer())
|
||||
})
|
||||
})
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Import
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
const Vector2 = Math.Import('Vector2')
|
||||
const Vector3 = Math.Import('Vector2')
|
||||
const Vertex = Math.Import('Vertex')
|
||||
const Geometry = Math.Import('Geometry')
|
||||
```
|
||||
|
||||
## PartialDeep
|
||||
|
||||
|
||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "@sinclair/typebox",
|
||||
"version": "0.33.22",
|
||||
"version": "0.34.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "@sinclair/typebox",
|
||||
"version": "0.33.22",
|
||||
"version": "0.34.0",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@arethetypeswrong/cli": "^0.13.2",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@sinclair/typebox",
|
||||
"version": "0.33.22",
|
||||
"version": "0.34.0",
|
||||
"description": "Json Schema Type Builder with Static Type Resolution for TypeScript",
|
||||
"keywords": [
|
||||
"typescript",
|
||||
|
||||
210
readme.md
210
readme.md
@@ -68,8 +68,7 @@ License MIT
|
||||
- [Options](#types-options)
|
||||
- [Properties](#types-properties)
|
||||
- [Generics](#types-generics)
|
||||
- [References](#types-references)
|
||||
- [Recursive](#types-recursive)
|
||||
- [Modules](#types-modules)
|
||||
- [Template Literal](#types-template-literal)
|
||||
- [Indexed](#types-indexed)
|
||||
- [Mapped](#types-mapped)
|
||||
@@ -79,7 +78,9 @@ License MIT
|
||||
- [Unsafe](#types-unsafe)
|
||||
- [Syntax](#syntax)
|
||||
- [Parse](#syntax-parse)
|
||||
- [Compose](#syntax-compose)
|
||||
- [Context](#syntax-context)
|
||||
- [Module](#syntax-module)
|
||||
- [Static](#syntax-static)
|
||||
- [Limits](#syntax-limits)
|
||||
- [Values](#values)
|
||||
@@ -769,103 +770,40 @@ const T = Nullable(Type.String()) // const T = {
|
||||
type T = Static<typeof T> // type T = string | null
|
||||
```
|
||||
|
||||
<a name='types-references'></a>
|
||||
<a name='types-modules'></a>
|
||||
|
||||
### Reference Types
|
||||
### Module Types
|
||||
|
||||
Reference types can be created with Ref. These types infer the same as the target type but only store a named `$ref` to the target type.
|
||||
TypeBox Modules are containers for related types. They function as namespaces and enable internal types to reference each other via string references. Modules support both singular and mutually recursive types. They provide a mechanism to create circular types irrespective of the order in which types are defined.
|
||||
|
||||
```typescript
|
||||
const Vector = Type.Object({ // const Vector = {
|
||||
x: Type.Number(), // type: 'object',
|
||||
y: Type.Number(), // required: ['x', 'y', 'z'],
|
||||
}, { $id: 'Vector' }) // properties: {
|
||||
// x: { type: 'number' },
|
||||
// y: { type: 'number' }
|
||||
// },
|
||||
// $id: 'Vector'
|
||||
// }
|
||||
// The following creates a circular recursive type.
|
||||
|
||||
const VectorRef = Type.Ref(Vector) // const VectorRef = {
|
||||
// $ref: 'Vector'
|
||||
// }
|
||||
const Module = Type.Module({
|
||||
A: Type.Object({
|
||||
b: Type.Ref('B') // Ref B:
|
||||
}),
|
||||
B: Type.Object({
|
||||
c: Type.Ref('C') // Ref C:
|
||||
}),
|
||||
C: Type.Object({
|
||||
a: Type.Ref('A') // Ref A:
|
||||
}),
|
||||
})
|
||||
|
||||
type VectorRef = Static<typeof VectorRef> // type VectorRef = {
|
||||
// x: number,
|
||||
// y: number
|
||||
// }
|
||||
```
|
||||
Use Deref to dereference a type. This function will replace any interior reference with the target type.
|
||||
```typescript
|
||||
const Vertex = Type.Object({ // const Vertex = {
|
||||
position: VectorRef, // type: 'object',
|
||||
texcoord: VectorRef, // required: ['position', 'texcoord'],
|
||||
}) // properties: {
|
||||
// position: { $ref: 'Vector' },
|
||||
// texcoord: { $ref: 'Vector' }
|
||||
// }
|
||||
// }
|
||||
// Module types must be imported before use.
|
||||
|
||||
const VertexDeref = Type.Deref(Vertex, [Vector]) // const VertexDeref = {
|
||||
// type: 'object',
|
||||
// required: ['position', 'texcoord'],
|
||||
// properties: {
|
||||
// position: {
|
||||
// type: 'object',
|
||||
// required: ['x', 'y', 'z'],
|
||||
// properties: {
|
||||
// x: { type: 'number' },
|
||||
// y: { type: 'number' }
|
||||
// }
|
||||
// },
|
||||
// texcoord: {
|
||||
// type: 'object',
|
||||
// required: ['x', 'y', 'z'],
|
||||
// properties: {
|
||||
// x: { type: 'number' },
|
||||
// y: { type: 'number' }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
```
|
||||
Note that Ref types do not store structural information about the type they're referencing. Because of this, these types cannot be used with some mapping types (such as Partial or Pick). For applications that require mapping on Ref, use Deref to normalize the type first.
|
||||
const A = Module.Import('A') // const A: TImport<{...}, 'A'>
|
||||
|
||||
<a name='types-recursive'></a>
|
||||
|
||||
### Recursive Types
|
||||
|
||||
TypeBox supports recursive data structures with Recursive. This type wraps an interior type and provides it a `this` context that allows the type to reference itself. The following creates a recursive type. Singular recursive inference is also supported.
|
||||
|
||||
```typescript
|
||||
const Node = Type.Recursive(This => Type.Object({ // const Node = {
|
||||
id: Type.String(), // $id: 'Node',
|
||||
nodes: Type.Array(This) // type: 'object',
|
||||
}), { $id: 'Node' }) // properties: {
|
||||
// id: {
|
||||
// type: 'string'
|
||||
// },
|
||||
// nodes: {
|
||||
// type: 'array',
|
||||
// items: {
|
||||
// $ref: 'Node'
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
// required: [
|
||||
// 'id',
|
||||
// 'nodes'
|
||||
// ]
|
||||
// }
|
||||
|
||||
type Node = Static<typeof Node> // type Node = {
|
||||
// id: string
|
||||
// nodes: Node[]
|
||||
// }
|
||||
|
||||
function test(node: Node) {
|
||||
const id = node.nodes[0].nodes[0].id // id is string
|
||||
}
|
||||
type A = Static<typeof A> // type A = {
|
||||
// b: {
|
||||
// c: {
|
||||
// a: {
|
||||
// b: ...
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
```
|
||||
|
||||
<a name='types-template-literal'></a>
|
||||
@@ -1084,11 +1022,11 @@ if(TypeGuard.IsString(T)) {
|
||||
|
||||
<a name='syntax'></a>
|
||||
|
||||
## Syntax
|
||||
## Syntax Types
|
||||
|
||||
TypeBox supports parsing TypeScript type annotation syntax directly into TypeBox types, providing an alternative to the Type.* API for type construction. This feature functions at runtime and within the TypeScript type system, enabling types encoded as strings to be inferred as equivalent TypeBox types.
|
||||
TypeBox provides support for Syntax Types, enabling it to parse TypeScript syntax directly into TypeBox types. Syntax Types serve as a DSL frontend for TypeBox's type builder and are useful for converting existing TypeScript type definitions into Json Schema schematics.
|
||||
|
||||
TypeScript syntax parsing is provided as an optional import.
|
||||
Syntax Types are provided via optional import.
|
||||
|
||||
```typescript
|
||||
import { Parse } from '@sinclair/typebox/syntax'
|
||||
@@ -1098,9 +1036,7 @@ import { Parse } from '@sinclair/typebox/syntax'
|
||||
|
||||
### Parse
|
||||
|
||||
The Parse function can be used to parse TypeScript code into TypeBox types. The left hand result will be a inferred TypeBox type of TSchema. Invalid syntax will result in an undefined return value.
|
||||
|
||||
[TypeScript Link Here](https://www.typescriptlang.org/play/?moduleResolution=99&module=199#code/JYWwDg9gTgLgBAbzgBQIZQM4FM4F84BmUEIcA5AAIbAB2AxgDarBQD0MAnmFgEYQAerDBxoxU-MgChJdCDQzwAgnAC8KdNgAUZBVFoBzMgEo4ps+YuXLrVnFnylALjgAVAMow9NfdPsK4AEKq6phY2gDaAIwANHAATLEAzAC6xlbpGTZ2cv4Bzi4uAK5gDFgAPOGSGdU1tdVZpi4AMsAwWFCoDGWRAHzRVXWDQ5m2jS1tHV1xfQPDc3MNruPtnWWJPbPzW7VZyRsyOfAAwsFooZoABkj8zjSFIDztsRy3949QeBcm6Vl+x-kAeR4ACssHQYGUEJttjCrIsbq4AHJvdrQ2Ho0yLF5IlFQNEY2FZXD7IA)
|
||||
Use the Parse function to convert a TypeScript string into a TypeBox type. TypeBox will infer the appropriate TSchema type or return undefined if there is a syntax error.
|
||||
|
||||
```typescript
|
||||
const A = Parse('string') // const A: TString
|
||||
@@ -1117,64 +1053,96 @@ const C = Parse(`{ x: number, y: number }`) // const C: TObject<{
|
||||
// }>
|
||||
```
|
||||
|
||||
|
||||
|
||||
<a name='syntax-compose'></a>
|
||||
|
||||
### Compose
|
||||
|
||||
Syntax Types are designed to be interchangeable with standard Types.
|
||||
|
||||
```typescript
|
||||
const T = Type.Object({ // const T: TObject<{
|
||||
x: Parse('number'), // x: TNumber,
|
||||
y: Parse('number'), // y: TNumber,
|
||||
z: Parse('number') // z: TNumber
|
||||
}) // }>
|
||||
```
|
||||
|
||||
<a name='syntax-module'></a>
|
||||
|
||||
### Module
|
||||
|
||||
Syntax Types support Module parsing, which is useful for processing multiple TypeScript types. Module parsing supports type alias and interface definitions. Generics are currently unsupported as of 0.34.0.
|
||||
|
||||
```typescript
|
||||
const Foo = Parse(`module Foo {
|
||||
|
||||
export type A = string
|
||||
|
||||
export type B = number
|
||||
|
||||
export type C = A | B
|
||||
|
||||
}`)
|
||||
|
||||
const C = Foo.Import('C') // const C: TImport<{
|
||||
// ...
|
||||
// }, 'C'>
|
||||
```
|
||||
|
||||
<a name='syntax-context'></a>
|
||||
|
||||
### Context
|
||||
|
||||
The Parse function accepts an optional context parameter that enables external types to be referenced within the syntax.
|
||||
|
||||
[TypeScript Link Here](https://www.typescriptlang.org/play/?moduleResolution=99&module=199#code/JYWwDg9gTgLgBAbzgBQIZQM4FM4F84BmUEIcA5AAIbAB2AxgDarBQD0MAnmFgEYQAerDBxoxU-MgChQkWIjgAVLjnxES5KrUbM2nbnwmTJdCDQzwFcALyLlAOgDyPAFZY6MABRI4P33-8BgayscCYArgwAJnA8OADuUMAwMFg0cKgYAFwo6NgeAAYIkj782UrcdgByYSCxUB4AlAA0ga1tbcG+pXA0NXVNxXAcZfbVtVj1ze3TM50+wz19EwM+AF4jFWN1jTO7rXNr2b3jUJK4DXuXV-6duPkNRiZm8ACC1jmYWF6KeC35aLBgKgGAAeBQAPnuV06T3McBeZScrncIKK13R1wO3QUDjAMGApmBYK2E3BKwxFNmIXmiLxBJoRIUJKgZMGlPZQWpcHWilx+MJoKZSxZbI5Yp8t3Bj1McIAQu8AXkkJZcH8ANZYDgQAiKKHomEy+CysoAVRo9JBAG1ReKOQcFAAZJITIlkCSs222+1OlJQV0cMgez1i73Ov2gsirQM24MYzoAXSlxkNcAAwgrcl9lb84PlLAAyeRxI7CvB6zmhFOpsoASVEE2wKMtOJcbhgqJjscxXLg2OZAG5O13LgchmUB0Ph7tRzyhSdB1OKZKWi3ke20Yv9T3i4oJ5ut3hwYmjEA)
|
||||
The Parse function accepts an initial Context argument, allowing external types to be passed into the parser.
|
||||
|
||||
```typescript
|
||||
const T = Type.Object({ // could be written as: Parse(`{
|
||||
x: Type.Number(), // x: number,
|
||||
y: Type.Number(), // y: number,
|
||||
z: Type.Number() // z: number
|
||||
z: Type.Number() // z: number
|
||||
}) // }`)
|
||||
|
||||
const A = Parse({ T }, `Partial<T>`) // const A: TObject<{
|
||||
const A = Parse({ T }, 'Partial<T>') // const A: TObject<{
|
||||
// x: TOptional<TNumber>,
|
||||
// y: TOptional<TNumber>,
|
||||
// z: TOptional<TNumber>
|
||||
// }>
|
||||
|
||||
const B = Parse({ T }, `keyof T`) // const B: TUnion<[
|
||||
const B = Parse({ T }, 'keyof T') // const B: TUnion<[
|
||||
// TLiteral<'x'>,
|
||||
// TLiteral<'y'>,
|
||||
// TLiteral<'z'>
|
||||
// ]>
|
||||
|
||||
const C = Parse({ T }, `T & { w: number }`) // const C: TIntersect<[TObject<{
|
||||
const C = Parse({ T }, 'T & { w: number }') // const C: TIntersect<[TObject<{
|
||||
// x: TNumber;
|
||||
// y: TNumber;
|
||||
// z: TNumber;
|
||||
// }>, TObject<{
|
||||
// w: TNumber;
|
||||
// }>]>
|
||||
|
||||
|
||||
```
|
||||
|
||||
<a name='syntax-static'></a>
|
||||
|
||||
### Static
|
||||
|
||||
TypeBox provides two Static types for inferring TypeScript syntax directly from strings.
|
||||
|
||||
[TypeScript Link Here](https://www.typescriptlang.org/play/?moduleResolution=99&module=199#code/JYWwDg9gTgLgBAbzgZRgQxsAxgBTVAZwFMBBA5LACyJDQBoV1Nd9iyAVATzCLgF84AMygQQcAOQABAsAB2WADZpgUAPQxuRAEYQAHqoKdZ6XeLgAoc6tVwA6sAUK4cwUShw0BD3HYVqtSw0eFDgAXkYMbDxCUnIqGjQAHgQ+BgADJF0ALjhZAFcQLTd+NIA+OArrOCDeZBz2AHktACsiLBhk8wrunt6+-oHBgaqK7J8AOQKiqC6hufmF3qq+Ussq+0dnWVd3T28awM0fMIjmaLYCLh5k1LgMuDH8wuK+MqWbGuPwhFnFv--3qMck9pr8AeDFiM4EA)
|
||||
Syntax Types provide two Static types for inferring TypeScript syntax from strings.
|
||||
|
||||
```typescript
|
||||
import { StaticParseAsSchema, StaticParseAsType } from '@sinclair/typebox/syntax'
|
||||
|
||||
// Will infer as a TSchema
|
||||
|
||||
type S = StaticParseAsSchema<{}, `{ x: number }`> // type S: TObject<{
|
||||
type S = StaticParseAsSchema<{}, '{ x: number }'> // type S: TObject<{
|
||||
// x: TNumber
|
||||
// }>
|
||||
|
||||
// Will infer as a type
|
||||
|
||||
type T = StaticParseAsType<{}, `{ x: number }`> // type T = {
|
||||
type T = StaticParseAsType<{}, '{ x: number }'> // type T = {
|
||||
// x: number
|
||||
//
|
||||
```
|
||||
@@ -1182,9 +1150,9 @@ type T = StaticParseAsType<{}, `{ x: number }`> // type T = {
|
||||
|
||||
<a name='syntax-limits'></a>
|
||||
|
||||
### Limits
|
||||
### Limitations
|
||||
|
||||
The Parse function works by TypeBox parsing TypeScript syntax within the type system itself. This can place some additional demand on the TypeScript compiler, which may affect language service (LSP) responsiveness when working with especially large or complex types. In particular, very wide structures or deeply nested types may approach TypeScript’s instantiation limits.
|
||||
Syntax Types work by having TypeBox parse TypeScript syntax within the TypeScript type system. This approach can place some strain on the TypeScript compiler and language service, potentially affecting responsiveness. While TypeBox makes a best-effort attempt to optimize for Syntax Types, users should be mindful of the following structures:
|
||||
|
||||
```typescript
|
||||
// Excessively wide structures will result in instantiation limits exceeding
|
||||
@@ -1211,12 +1179,12 @@ const B = Parse(`{
|
||||
}`)
|
||||
```
|
||||
|
||||
For parsing especially complex types, TypeBox provides the ParseOnly function, which parses strings at runtime and returns a non-inferred TSchema type. ParseOnly allows for arbitrarily complex types without reaching instantiation limits, making it useful for cases where TypeScript types need to be directly converted to Json Schema.
|
||||
In cases where Syntax Types busts through TypeScript instantiation limits, TypeBox offers a fallback ParseOnly function which will Parse the types at runtime, but not infer the type. This function can also be used for parsing non-constant strings.
|
||||
|
||||
```typescript
|
||||
import { ParseOnly } from '@sinclair/typebox/syntax'
|
||||
|
||||
// ok: but where A is TSchema | undefined
|
||||
// Where A is TSchema | undefined
|
||||
|
||||
const A = ParseOnly(`{
|
||||
x: {
|
||||
@@ -1229,7 +1197,7 @@ const A = ParseOnly(`{
|
||||
}`)
|
||||
```
|
||||
|
||||
Optimizing TypeScript string parsing performance is an ongoing area of research. For more information, see the [ParseBox](https://github.com/sinclairzx81/parsebox) project, which documents TypeBox’s parsing infrastructure and focuses efforts to improve parsing performance.
|
||||
For more information on TypeBox's parsing infrastructure, refer to the [ParseBox](https://github.com/sinclairzx81/parsebox) project.
|
||||
|
||||
<a name='values'></a>
|
||||
|
||||
@@ -1914,12 +1882,12 @@ The following table lists esbuild compiled and minified sizes for each TypeBox m
|
||||
┌──────────────────────┬────────────┬────────────┬─────────────┐
|
||||
│ (index) │ Compiled │ Minified │ Compression │
|
||||
├──────────────────────┼────────────┼────────────┼─────────────┤
|
||||
│ typebox/compiler │ '119.8 kb' │ ' 52.6 kb' │ '2.28 x' │
|
||||
│ typebox/errors │ ' 74.4 kb' │ ' 33.1 kb' │ '2.25 x' │
|
||||
│ typebox/syntax │ '115.3 kb' │ ' 48.3 kb' │ '2.39 x' │
|
||||
│ typebox/compiler │ '121.7 kb' │ ' 53.4 kb' │ '2.28 x' │
|
||||
│ typebox/errors │ ' 75.3 kb' │ ' 33.4 kb' │ '2.25 x' │
|
||||
│ typebox/syntax │ '120.1 kb' │ ' 50.5 kb' │ '2.38 x' │
|
||||
│ typebox/system │ ' 7.4 kb' │ ' 3.2 kb' │ '2.33 x' │
|
||||
│ typebox/value │ '157.2 kb' │ ' 66.1 kb' │ '2.38 x' │
|
||||
│ typebox │ ' 98.9 kb' │ ' 41.2 kb' │ '2.40 x' │
|
||||
│ typebox/value │ '160.3 kb' │ ' 67.4 kb' │ '2.38 x' │
|
||||
│ typebox │ ' 96.2 kb' │ ' 40.2 kb' │ '2.39 x' │
|
||||
└──────────────────────┴────────────┴────────────┴─────────────┘
|
||||
```
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ import type { TBoolean } from '../type/boolean/index'
|
||||
import type { TDate } from '../type/date/index'
|
||||
import type { TConstructor } from '../type/constructor/index'
|
||||
import type { TFunction } from '../type/function/index'
|
||||
import type { TImport } from '../type/module/index'
|
||||
import type { TInteger } from '../type/integer/index'
|
||||
import type { TIntersect } from '../type/intersect/index'
|
||||
import type { TIterator } from '../type/iterator/index'
|
||||
@@ -110,7 +111,7 @@ export class TypeCheck<T extends TSchema> {
|
||||
return (this.hasTransform ? TransformDecode(this.schema, this.references, value) : value) as never
|
||||
}
|
||||
/** Encodes a value or throws if error */
|
||||
public Encode<Static = StaticDecode<T>, Result extends Static = Static>(value: unknown): Result {
|
||||
public Encode<Static = StaticEncode<T>, Result extends Static = Static>(value: unknown): Result {
|
||||
const encoded = this.hasTransform ? TransformEncode(this.schema, this.references, value) : value
|
||||
if (!this.checkFunc(encoded)) throw new TransformEncodeCheckError(this.schema, value, this.Errors(value).First()!)
|
||||
return encoded as never
|
||||
@@ -288,6 +289,11 @@ export namespace TypeCompiler {
|
||||
function* FromFunction(schema: TFunction, references: TSchema[], value: string): IterableIterator<string> {
|
||||
yield `(typeof ${value} === 'function')`
|
||||
}
|
||||
function* FromImport(schema: TImport, references: TSchema[], value: string): IterableIterator<string> {
|
||||
const definitions = globalThis.Object.values(schema.$defs) as TSchema[]
|
||||
const target = schema.$defs[schema.$ref] as TSchema
|
||||
yield* Visit(target, [...references, ...definitions], value)
|
||||
}
|
||||
function* FromInteger(schema: TInteger, references: TSchema[], value: string): IterableIterator<string> {
|
||||
yield `Number.isInteger(${value})`
|
||||
if (IsNumber(schema.exclusiveMaximum)) yield `${value} < ${schema.exclusiveMaximum}`
|
||||
@@ -385,8 +391,12 @@ export namespace TypeCompiler {
|
||||
function* FromRef(schema: TRef, references: TSchema[], value: string): IterableIterator<string> {
|
||||
const target = Deref(schema, references)
|
||||
// Reference: If we have seen this reference before we can just yield and return the function call.
|
||||
// If this isn't the case we defer to visit to generate and set the function for subsequent passes.
|
||||
if (state.functions.has(schema.$ref)) return yield `${CreateFunctionName(schema.$ref)}(${value})`
|
||||
// If this isn't the case we defer to visit to generate and set the _recursion_end_for_ for subsequent
|
||||
// passes. This operation is very awkward as we are using the functions state to store values to
|
||||
// enable self referential types to terminate. This needs to be refactored.
|
||||
const recursiveEnd = `_recursion_end_for_${schema.$ref}`
|
||||
if (state.functions.has(recursiveEnd)) return yield `${CreateFunctionName(schema.$ref)}(${value})`
|
||||
state.functions.set(recursiveEnd, '') // terminate recursion here by setting the name.
|
||||
yield* Visit(target, references, value)
|
||||
}
|
||||
function* FromRegExp(schema: TRegExp, references: TSchema[], value: string): IterableIterator<string> {
|
||||
@@ -485,6 +495,8 @@ export namespace TypeCompiler {
|
||||
return yield* FromDate(schema_, references_, value)
|
||||
case 'Function':
|
||||
return yield* FromFunction(schema_, references_, value)
|
||||
case 'Import':
|
||||
return yield* FromImport(schema_, references_, value)
|
||||
case 'Integer':
|
||||
return yield* FromInteger(schema_, references_, value)
|
||||
case 'Intersect':
|
||||
|
||||
@@ -46,6 +46,7 @@ import type { TBoolean } from '../type/boolean/index'
|
||||
import type { TDate } from '../type/date/index'
|
||||
import type { TConstructor } from '../type/constructor/index'
|
||||
import type { TFunction } from '../type/function/index'
|
||||
import type { TImport } from '../type/module/index'
|
||||
import type { TInteger } from '../type/integer/index'
|
||||
import type { TIntersect } from '../type/intersect/index'
|
||||
import type { TIterator } from '../type/iterator/index'
|
||||
@@ -302,6 +303,11 @@ function* FromDate(schema: TDate, references: TSchema[], path: string, value: an
|
||||
function* FromFunction(schema: TFunction, references: TSchema[], path: string, value: any): IterableIterator<ValueError> {
|
||||
if (!IsFunction(value)) yield Create(ValueErrorType.Function, schema, path, value)
|
||||
}
|
||||
function* FromImport(schema: TImport, references: TSchema[], path: string, value: any): IterableIterator<ValueError> {
|
||||
const definitions = globalThis.Object.values(schema.$defs) as TSchema[]
|
||||
const target = schema.$defs[schema.$ref] as TSchema
|
||||
yield* Visit(target, [...references, ...definitions], path, value)
|
||||
}
|
||||
function* FromInteger(schema: TInteger, references: TSchema[], path: string, value: any): IterableIterator<ValueError> {
|
||||
if (!IsInteger(value)) return yield Create(ValueErrorType.Integer, schema, path, value)
|
||||
if (IsDefined<number>(schema.exclusiveMaximum) && !(value < schema.exclusiveMaximum)) {
|
||||
@@ -566,6 +572,8 @@ function* Visit<T extends TSchema>(schema: T, references: TSchema[], path: strin
|
||||
return yield* FromDate(schema_, references_, path, value)
|
||||
case 'Function':
|
||||
return yield* FromFunction(schema_, references_, path, value)
|
||||
case 'Import':
|
||||
return yield* FromImport(schema_, references_, path, value)
|
||||
case 'Integer':
|
||||
return yield* FromInteger(schema_, references_, path, value)
|
||||
case 'Intersect':
|
||||
|
||||
@@ -53,7 +53,6 @@ export * from './type/const/index'
|
||||
export * from './type/constructor/index'
|
||||
export * from './type/constructor-parameters/index'
|
||||
export * from './type/date/index'
|
||||
export * from './type/deref/index'
|
||||
export * from './type/enum/index'
|
||||
export * from './type/exclude/index'
|
||||
export * from './type/extends/index'
|
||||
@@ -67,6 +66,7 @@ export * from './type/iterator/index'
|
||||
export * from './type/intrinsic/index'
|
||||
export * from './type/keyof/index'
|
||||
export * from './type/literal/index'
|
||||
export * from './type/module/index'
|
||||
export * from './type/mapped/index'
|
||||
export * from './type/never/index'
|
||||
export * from './type/not/index'
|
||||
@@ -90,7 +90,6 @@ export * from './type/rest/index'
|
||||
export * from './type/return-type/index'
|
||||
export * from './type/schema/index'
|
||||
export * from './type/static/index'
|
||||
export * from './type/strict/index'
|
||||
export * from './type/string/index'
|
||||
export * from './type/symbol/index'
|
||||
export * from './type/template-literal/index'
|
||||
|
||||
@@ -26,36 +26,35 @@ THE SOFTWARE.
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
import * as Types from '../type/index'
|
||||
import { Static } from './parsebox/index'
|
||||
import { CreateType } from '../type/create/type'
|
||||
import { TSchema, SchemaOptions } from '../type/schema/index'
|
||||
import { StaticDecode } from '../type/static/index'
|
||||
import { Module } from './runtime'
|
||||
import { Type } from './static'
|
||||
import { Main } from './static'
|
||||
|
||||
/** `[Experimental]` Infers a TypeBox type from TypeScript syntax. */
|
||||
export type StaticParseAsSchema<Context extends Record<PropertyKey, TSchema>, Code extends string> = Static.Parse<Type, Code, Context>[0]
|
||||
/** `[Syntax]` Infers a TypeBox type from TypeScript syntax. */
|
||||
export type StaticParseAsSchema<Context extends Record<PropertyKey, Types.TSchema>, Code extends string> = Static.Parse<Main, Code, Context>[0]
|
||||
|
||||
/** `[Experimental]` Infers a TypeScript type from TypeScript syntax. */
|
||||
export type StaticParseAsType<Context extends Record<PropertyKey, TSchema>, Code extends string> = StaticParseAsSchema<Context, Code> extends infer Type extends TSchema ? StaticDecode<Type> : undefined
|
||||
/** `[Syntax]` Infers a TypeScript type from TypeScript syntax. */
|
||||
export type StaticParseAsType<Context extends Record<PropertyKey, Types.TSchema>, Code extends string> = StaticParseAsSchema<Context, Code> extends infer Type extends Types.TSchema ? Types.StaticDecode<Type> : undefined
|
||||
|
||||
/** `[Experimental]` Parses a TypeBox type from TypeScript syntax. */
|
||||
export function Parse<Context extends Record<PropertyKey, TSchema>, Code extends string>(context: Context, code: Code, options?: SchemaOptions): StaticParseAsSchema<Context, Code>
|
||||
/** `[Experimental]` Parses a TypeBox type from TypeScript syntax. */
|
||||
export function Parse<Code extends string>(code: Code, options?: SchemaOptions): StaticParseAsSchema<{}, Code>
|
||||
/** `[Experimental]` Parses a TypeBox type from TypeScript syntax. */
|
||||
/** `[Syntax]` Parses a TypeBox type from TypeScript syntax. */
|
||||
export function Parse<Context extends Record<PropertyKey, Types.TSchema>, Code extends string>(context: Context, code: Code, options?: Types.SchemaOptions): StaticParseAsSchema<Context, Code>
|
||||
/** `[Syntax]` Parses a TypeBox type from TypeScript syntax. */
|
||||
export function Parse<Code extends string>(code: Code, options?: Types.SchemaOptions): StaticParseAsSchema<{}, Code>
|
||||
/** `[Syntax]` Parses a TypeBox type from TypeScript syntax. */
|
||||
export function Parse(...args: any[]): never {
|
||||
return ParseOnly.apply(null, args as never) as never
|
||||
}
|
||||
|
||||
/** `[Experimental]` Parses a TypeBox TSchema from TypeScript syntax. This function does not infer the type. */
|
||||
export function ParseOnly<Context extends Record<PropertyKey, TSchema>, Code extends string>(context: Context, code: Code, options?: SchemaOptions): TSchema | undefined
|
||||
/** `[Experimental]` Parses a TypeBox TSchema from TypeScript syntax */
|
||||
export function ParseOnly<Code extends string>(code: Code, options?: SchemaOptions): TSchema | undefined
|
||||
/** `[Experimental]` Parses a TypeBox TSchema from TypeScript syntax. This function does not infer the type. */
|
||||
export function ParseOnly(...args: any[]): TSchema | undefined {
|
||||
/** `[Syntax]` Parses a TypeBox TSchema from TypeScript syntax. This function does not infer the type. */
|
||||
export function ParseOnly<Context extends Record<PropertyKey, Types.TSchema>, Code extends string>(context: Context, code: Code, options?: Types.SchemaOptions): Types.TSchema | undefined
|
||||
/** `[Syntax]` Parses a TypeBox TSchema from TypeScript syntax */
|
||||
export function ParseOnly<Code extends string>(code: Code, options?: Types.SchemaOptions): Types.TSchema | undefined
|
||||
/** `[Syntax]` Parses a TypeBox TSchema from TypeScript syntax. This function does not infer the type. */
|
||||
export function ParseOnly(...args: any[]): Types.TSchema | undefined {
|
||||
const withContext = typeof args[0] === 'string' ? false : true
|
||||
const [context, code, options] = withContext ? [args[0], args[1], args[2] || {}] : [{}, args[0], args[1] || {}]
|
||||
const type = Module.Parse('Type', code, context)[0] as TSchema | undefined
|
||||
return (type !== undefined ? CreateType(type, options) : undefined) as never
|
||||
const type = Module.Parse('Main', code, context)[0] as Types.TSchema | undefined
|
||||
// Note: Parsing may return either a ModuleInstance or Type. We only apply options on the Type.
|
||||
return Types.KindGuard.IsSchema(type) ? Types.CloneType(type, options) : type
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ const SemiColon = ';'
|
||||
const SingleQuote = "'"
|
||||
const DoubleQuote = '"'
|
||||
const Tilde = '`'
|
||||
const Equals = '='
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// DestructureRight
|
||||
@@ -59,13 +60,140 @@ function DestructureRight<T>(values: T[]): [T[], T | undefined] {
|
||||
: [values, undefined]
|
||||
}
|
||||
// ------------------------------------------------------------------
|
||||
// Deref
|
||||
// ------------------------------------------------------------------
|
||||
const Deref = (context: Types.TProperties, key: string): Types.TSchema => {
|
||||
return key in context ? context[key] : Types.Ref(key)
|
||||
}
|
||||
// ------------------------------------------------------------------
|
||||
// ExportModifier
|
||||
// ------------------------------------------------------------------
|
||||
// prettier-ignore
|
||||
const ExportModifierMapping = (values: unknown[]) => {
|
||||
return values.length === 1
|
||||
}
|
||||
// prettier-ignore
|
||||
const ExportModifier = Runtime.Union([
|
||||
Runtime.Tuple([Runtime.Const('export')]), Runtime.Tuple([])
|
||||
], ExportModifierMapping)
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// TypeAliasDeclaration
|
||||
// ------------------------------------------------------------------
|
||||
// prettier-ignore
|
||||
const TypeAliasDeclarationMapping = (_Export: boolean, _Keyword: 'type', Ident: string, _Equals: typeof Equals, Type: Types.TSchema) => {
|
||||
return { [Ident]: Type }
|
||||
}
|
||||
// prettier-ignore
|
||||
const TypeAliasDeclaration = Runtime.Tuple([
|
||||
Runtime.Ref<boolean>('ExportModifier'),
|
||||
Runtime.Const('type'),
|
||||
Runtime.Ident(),
|
||||
Runtime.Const(Equals),
|
||||
Runtime.Ref<Types.TSchema>('Type')
|
||||
], value => TypeAliasDeclarationMapping(...value))
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// HeritageList
|
||||
// ------------------------------------------------------------------
|
||||
// prettier-ignore (note, heritage list should disallow trailing comma)
|
||||
const HeritageListDelimiter = Runtime.Union([Runtime.Tuple([Runtime.Const(Comma), Runtime.Const(Newline)]), Runtime.Tuple([Runtime.Const(Comma)])])
|
||||
// prettier-ignore
|
||||
const HeritageListMapping = (values: string[], context: Types.TProperties): Types.TSchema[] => {
|
||||
return (
|
||||
values.length === 3 ? [Deref(context, values[0]), ...values[2] as never] :
|
||||
values.length === 1 ? [Deref(context, values[0])] :
|
||||
[]
|
||||
) as never
|
||||
}
|
||||
// prettier-ignore
|
||||
const HeritageList = Runtime.Union([
|
||||
Runtime.Tuple([Runtime.Ident(), HeritageListDelimiter, Runtime.Ref('HeritageList')]),
|
||||
Runtime.Tuple([Runtime.Ident()]),
|
||||
Runtime.Tuple([])
|
||||
], HeritageListMapping)
|
||||
// ------------------------------------------------------------------
|
||||
// Heritage
|
||||
// ------------------------------------------------------------------
|
||||
// prettier-ignore
|
||||
const HeritageMapping = (values: unknown[]): unknown[] => {
|
||||
return (values.length === 2 ? values[1] : []) as never
|
||||
}
|
||||
// prettier-ignore
|
||||
const Heritage = Runtime.Union([
|
||||
Runtime.Tuple([Runtime.Const('extends'), Runtime.Ref('HeritageList')]),
|
||||
Runtime.Tuple([])
|
||||
], HeritageMapping)
|
||||
// ------------------------------------------------------------------
|
||||
// InterfaceDeclaration
|
||||
// ------------------------------------------------------------------
|
||||
// prettier-ignore
|
||||
const InterfaceDeclarationMapping = (_0: boolean, _1: 'interface', Ident: string, Heritage: Types.TRef[], _4: typeof LBrace, Properties: Types.TProperties, _6: typeof RBrace) => {
|
||||
return { [Ident]: Types.Intersect([...Heritage, Types.Object(Properties)]) }
|
||||
}
|
||||
// prettier-ignore
|
||||
const InterfaceDeclaration = Runtime.Tuple([
|
||||
Runtime.Ref<boolean>('ExportModifier'),
|
||||
Runtime.Const('interface'),
|
||||
Runtime.Ident(),
|
||||
Runtime.Ref<Types.TRef[]>('Heritage'),
|
||||
Runtime.Const(LBrace),
|
||||
Runtime.Ref<Types.TProperties>('Properties'),
|
||||
Runtime.Const(RBrace),
|
||||
], values => InterfaceDeclarationMapping(...values))
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// ModuleType
|
||||
// ------------------------------------------------------------------
|
||||
// prettier-ignore
|
||||
const ModuleType = Runtime.Union([
|
||||
Runtime.Ref('InterfaceDeclaration'),
|
||||
Runtime.Ref('TypeAliasDeclaration')
|
||||
])
|
||||
// ------------------------------------------------------------------
|
||||
// ModuleProperties
|
||||
// ------------------------------------------------------------------
|
||||
// prettier-ignore
|
||||
const ModulePropertiesDelimiter = Runtime.Union([
|
||||
Runtime.Tuple([Runtime.Const(SemiColon), Runtime.Const(Newline)]),
|
||||
Runtime.Tuple([Runtime.Const(SemiColon)]),
|
||||
Runtime.Tuple([Runtime.Const(Newline)]),
|
||||
])
|
||||
// prettier-ignore
|
||||
const ModulePropertiesMapping = (values: unknown[]): Types.TProperties => {
|
||||
return (
|
||||
values.length === 3 ? { ...values[0] as Types.TProperties, ...values[2] as Types.TProperties }:
|
||||
values.length === 1 ? values[0] as Types.TProperties :
|
||||
{} as Types.TProperties
|
||||
)
|
||||
}
|
||||
// prettier-ignore
|
||||
const ModuleProperties = Runtime.Union([
|
||||
Runtime.Tuple([Runtime.Ref<Types.TProperties>('ModuleType'), ModulePropertiesDelimiter, Runtime.Ref<Types.TProperties>('ModuleProperties')]),
|
||||
Runtime.Tuple([Runtime.Ref<Types.TProperties>('ModuleType')]),
|
||||
Runtime.Tuple([]),
|
||||
], ModulePropertiesMapping)
|
||||
// ------------------------------------------------------------------
|
||||
// ModuleDeclaration
|
||||
// ------------------------------------------------------------------
|
||||
// prettier-ignore
|
||||
const ModuleIdentifier = Runtime.Union([
|
||||
Runtime.Tuple([Runtime.Ident()]),
|
||||
Runtime.Tuple([])
|
||||
])
|
||||
// prettier-ignore
|
||||
const ModuleDeclarationMapping = (_1: boolean, _2: 'module', _Ident: string[], _3: typeof LBrace, Properties: Types.TProperties, _5: typeof RBrace) => {
|
||||
return Types.Module(Properties)
|
||||
}
|
||||
// prettier-ignore
|
||||
const ModuleDeclaration = Runtime.Tuple([
|
||||
Runtime.Ref<boolean>('ExportModifier'), Runtime.Const('module'), ModuleIdentifier, Runtime.Const(LBrace), Runtime.Ref<Types.TProperties>('ModuleProperties'), Runtime.Const(RBrace)
|
||||
], values => ModuleDeclarationMapping(...values))
|
||||
// ------------------------------------------------------------------
|
||||
// Reference
|
||||
// ------------------------------------------------------------------
|
||||
// prettier-ignore
|
||||
const Reference = Runtime.Ident((value, context: Record<PropertyKey, Types.TSchema>) => {
|
||||
return value in context ? context[value] : Types.Ref(value)
|
||||
})
|
||||
|
||||
const Reference = Runtime.Ident((value, context: Types.TProperties) => Deref(context, value))
|
||||
// ------------------------------------------------------------------
|
||||
// Literal
|
||||
// ------------------------------------------------------------------
|
||||
@@ -75,7 +203,6 @@ const Literal = Runtime.Union([
|
||||
Runtime.Number(value => Types.Literal(parseFloat(value))),
|
||||
Runtime.String([SingleQuote, DoubleQuote, Tilde], value => Types.Literal(value))
|
||||
])
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Keyword
|
||||
// ------------------------------------------------------------------
|
||||
@@ -94,7 +221,6 @@ const Keyword = Runtime.Union([
|
||||
Runtime.Const('unknown', Runtime.As(Types.Unknown())),
|
||||
Runtime.Const('void', Runtime.As(Types.Void())),
|
||||
])
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// KeyOf
|
||||
// ------------------------------------------------------------------
|
||||
@@ -106,7 +232,6 @@ const KeyOfMapping = (values: unknown[]) => (
|
||||
const KeyOf = Runtime.Union([
|
||||
Runtime.Tuple([Runtime.Const('keyof')]), Runtime.Tuple([])
|
||||
], KeyOfMapping)
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// IndexArray
|
||||
// ------------------------------------------------------------------
|
||||
@@ -137,7 +262,6 @@ const Extends = Runtime.Union([
|
||||
Runtime.Tuple([Runtime.Const('extends'), Runtime.Ref('Type'), Runtime.Const(Question), Runtime.Ref('Type'), Runtime.Const(Colon), Runtime.Ref('Type')]),
|
||||
Runtime.Tuple([])
|
||||
], ExtendsMapping)
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Base
|
||||
// ------------------------------------------------------------------
|
||||
@@ -219,7 +343,6 @@ const Factor = Runtime.Tuple([
|
||||
Runtime.Ref<unknown[]>('IndexArray'),
|
||||
Runtime.Ref<Types.TSchema[]>('Extends')
|
||||
], values => FactorMapping(...values))
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Expr
|
||||
// ------------------------------------------------------------------
|
||||
@@ -266,30 +389,26 @@ const Expr = Runtime.Tuple([
|
||||
// Type
|
||||
// ------------------------------------------------------------------
|
||||
const Type = Runtime.Ref('Expr')
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Properties
|
||||
// ------------------------------------------------------------------
|
||||
// prettier-ignore
|
||||
const PropertyKey = Runtime.Union([Runtime.Ident(), Runtime.String([SingleQuote, DoubleQuote])])
|
||||
const Readonly = Runtime.Union([Runtime.Tuple([Runtime.Const('readonly')]), Runtime.Tuple([])], (value) => value.length > 0)
|
||||
const Optional = Runtime.Union([Runtime.Tuple([Runtime.Const(Question)]), Runtime.Tuple([])], (value) => value.length > 0)
|
||||
// prettier-ignore
|
||||
const PropertyReadonly = Runtime.Union([Runtime.Tuple([Runtime.Const('readonly')]), Runtime.Tuple([])], value => value.length > 0)
|
||||
// prettier-ignore
|
||||
const PropertyOptional = Runtime.Union([Runtime.Tuple([Runtime.Const(Question)]), Runtime.Tuple([])], value => value.length > 0)
|
||||
// prettier-ignore
|
||||
const PropertyMapping = (Readonly: boolean, Key: string, Optional: boolean, _: typeof Colon, Type: Types.TSchema) => ({
|
||||
const PropertyMapping = (IsReadonly: boolean, Key: string, IsOptional: boolean, _: typeof Colon, Type: Types.TSchema) => ({
|
||||
[Key]: (
|
||||
Readonly && Optional ? Types.ReadonlyOptional(Type) :
|
||||
Readonly && !Optional ? Types.Readonly(Type) :
|
||||
!Readonly && Optional ? Types.Optional(Type) :
|
||||
IsReadonly && IsOptional ? Types.ReadonlyOptional(Type) :
|
||||
IsReadonly && !IsOptional ? Types.Readonly(Type) :
|
||||
!IsReadonly && IsOptional ? Types.Optional(Type) :
|
||||
Type
|
||||
)
|
||||
})
|
||||
// prettier-ignore
|
||||
const Property = Runtime.Tuple([
|
||||
Runtime.Ref<boolean>('PropertyReadonly'),
|
||||
Runtime.Ref<boolean>('Readonly'),
|
||||
Runtime.Ref<string>('PropertyKey'),
|
||||
Runtime.Ref<boolean>('PropertyOptional'),
|
||||
Runtime.Ref<boolean>('Optional'),
|
||||
Runtime.Const(Colon),
|
||||
Runtime.Ref<Types.TSchema>('Type'),
|
||||
], value => PropertyMapping(...value))
|
||||
@@ -303,30 +422,27 @@ const PropertyDelimiter = Runtime.Union([
|
||||
])
|
||||
// prettier-ignore
|
||||
const Properties = Runtime.Union([
|
||||
Runtime.Tuple([Runtime.Ref('Property'), Runtime.Ref('PropertyDelimiter'), Runtime.Ref('Properties')]),
|
||||
Runtime.Tuple([Runtime.Ref('Property'), Runtime.Ref('PropertyDelimiter')]),
|
||||
Runtime.Tuple([Runtime.Ref('Property')]),
|
||||
Runtime.Tuple([Runtime.Ref<Types.TProperties>('Property'), Runtime.Ref('PropertyDelimiter'), Runtime.Ref<Types.TProperties>('Properties')]),
|
||||
Runtime.Tuple([Runtime.Ref<Types.TProperties>('Property'), Runtime.Ref('PropertyDelimiter')]),
|
||||
Runtime.Tuple([Runtime.Ref<Types.TProperties>('Property')]),
|
||||
Runtime.Tuple([])
|
||||
], values => (
|
||||
values.length === 3 ? [values[0], ...values[2] as unknown[]] :
|
||||
values.length === 2 ? [values[0]] :
|
||||
values.length === 1 ? [values[0]] :
|
||||
[]
|
||||
values.length === 3 ? { ...values[0], ...values[2] } :
|
||||
values.length === 2 ? values[0] :
|
||||
values.length === 1 ? values[0] :
|
||||
{}
|
||||
))
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Object
|
||||
// ------------------------------------------------------------------
|
||||
// prettier-ignore
|
||||
const ObjectMapping = (values: Record<string, Types.TSchema>[]) => Types.Object(values.reduce((properties, record) => {
|
||||
return { ...properties, ...record }
|
||||
}, {} as Types.TProperties))
|
||||
const ObjectMapping = (_0: typeof LBrace, Properties: Types.TProperties, _2: typeof RBrace) => Types.Object(Properties)
|
||||
// prettier-ignore
|
||||
const _Object = Runtime.Tuple([
|
||||
Runtime.Const(LBrace),
|
||||
Runtime.Ref<Record<string, Types.TSchema>[]>('Properties'),
|
||||
Runtime.Ref<Types.TProperties>('Properties'),
|
||||
Runtime.Const(RBrace)
|
||||
], values => ObjectMapping(values[1]))
|
||||
], values => ObjectMapping(...values))
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Tuple
|
||||
@@ -401,7 +517,15 @@ const MappedMapping = (values: unknown[]) => {
|
||||
}
|
||||
// prettier-ignore
|
||||
const Mapped = Runtime.Tuple([
|
||||
Runtime.Const(LBrace), Runtime.Const(LBracket), Runtime.Ident(), Runtime.Const('in'), Runtime.Ref<Types.TSchema>('Type'), Runtime.Const(RBracket), Runtime.Const(Colon), Runtime.Ref<Types.TSchema>('Type'), Runtime.Const(RBrace)
|
||||
Runtime.Const(LBrace),
|
||||
Runtime.Const(LBracket),
|
||||
Runtime.Ident(),
|
||||
Runtime.Const('in'),
|
||||
Runtime.Ref<Types.TSchema>('Type'),
|
||||
Runtime.Const(RBracket),
|
||||
Runtime.Const(Colon),
|
||||
Runtime.Ref<Types.TSchema>('Type'),
|
||||
Runtime.Const(RBrace)
|
||||
], MappedMapping)
|
||||
// ------------------------------------------------------------------
|
||||
// AsyncIterator
|
||||
@@ -621,11 +745,37 @@ const Date = Runtime.Const('Date', Runtime.As(Types.Date()))
|
||||
// Uint8Array
|
||||
// ------------------------------------------------------------------
|
||||
const Uint8Array = Runtime.Const('Uint8Array', Runtime.As(Types.Uint8Array()))
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Main
|
||||
// ------------------------------------------------------------------
|
||||
// prettier-ignore
|
||||
const Main = Runtime.Union([
|
||||
ModuleDeclaration,
|
||||
TypeAliasDeclaration,
|
||||
InterfaceDeclaration,
|
||||
Type
|
||||
])
|
||||
// ------------------------------------------------------------------
|
||||
// Module
|
||||
// ------------------------------------------------------------------
|
||||
// prettier-ignore
|
||||
export const Module = new Runtime.Module({
|
||||
// ----------------------------------------------------------------
|
||||
// Modules, Interfaces and Type Aliases
|
||||
// ----------------------------------------------------------------
|
||||
ExportModifier,
|
||||
HeritageList,
|
||||
Heritage,
|
||||
InterfaceDeclaration,
|
||||
TypeAliasDeclaration,
|
||||
ModuleType,
|
||||
ModuleProperties,
|
||||
ModuleDeclaration,
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Type Expressions
|
||||
// ----------------------------------------------------------------
|
||||
Literal,
|
||||
Keyword,
|
||||
KeyOf,
|
||||
@@ -637,10 +787,10 @@ export const Module = new Runtime.Module({
|
||||
ExprTerm,
|
||||
ExprTail,
|
||||
Expr,
|
||||
Type,
|
||||
Type, // Alias for Expr
|
||||
PropertyKey,
|
||||
PropertyReadonly,
|
||||
PropertyOptional,
|
||||
Readonly,
|
||||
Optional,
|
||||
Property,
|
||||
PropertyDelimiter,
|
||||
Properties,
|
||||
@@ -674,5 +824,10 @@ export const Module = new Runtime.Module({
|
||||
Uncapitalize,
|
||||
Date,
|
||||
Uint8Array,
|
||||
Reference
|
||||
Reference,
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Main
|
||||
// ----------------------------------------------------------------
|
||||
Main
|
||||
})
|
||||
|
||||
@@ -48,6 +48,7 @@ type SemiColon = ';'
|
||||
type SingleQuote = "'"
|
||||
type DoubleQuote = '"'
|
||||
type Tilde = '`'
|
||||
type Equals = '='
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Delimit
|
||||
@@ -135,18 +136,156 @@ type Delimit<Parser extends Static.IParser, Delimiter extends Static.IParser> =
|
||||
], DelimitMapping>
|
||||
)
|
||||
// ------------------------------------------------------------------
|
||||
// Deref
|
||||
// ------------------------------------------------------------------
|
||||
// prettier-ignore
|
||||
type Deref<Context extends Types.TProperties, Ref extends string> = (
|
||||
Ref extends keyof Context ? Context[Ref] : Types.TRef<Ref>
|
||||
)
|
||||
// ------------------------------------------------------------------
|
||||
// ExportModifier
|
||||
// ------------------------------------------------------------------
|
||||
// prettier-ignore
|
||||
interface ExportModifierMapping extends Static.IMapping {
|
||||
output: this['input'] extends [string] ? true : false
|
||||
}
|
||||
// prettier-ignore
|
||||
type ExportModifier = Static.Union<[
|
||||
Static.Tuple<[Static.Const<'export'>]>,
|
||||
Static.Tuple<[]>,
|
||||
], ExportModifierMapping>
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// TypeAliasDeclaration
|
||||
// ------------------------------------------------------------------
|
||||
// prettier-ignore
|
||||
interface TypeAliasDeclarationMapping extends Static.IMapping {
|
||||
output: this['input'] extends [infer _Export extends boolean, 'type', infer Ident extends string, Equals, infer Type extends Types.TSchema]
|
||||
? { [_ in Ident]: Type }
|
||||
: never
|
||||
}
|
||||
// prettier-ignore
|
||||
type TypeAliasDeclaration = Static.Tuple<[
|
||||
ExportModifier, Static.Const<'type'>, Static.Ident, Static.Const<Equals>, Type
|
||||
], TypeAliasDeclarationMapping>
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// HeritageList
|
||||
// ------------------------------------------------------------------
|
||||
// prettier-ignore (note, heritage list should disallow trailing comma)
|
||||
type HeritageListDelimiter = Static.Union<[Static.Tuple<[Static.Const<Comma>, Static.Const<Newline>]>, Static.Tuple<[Static.Const<Comma>]>]>
|
||||
// prettier-ignore
|
||||
type HeritageListReduce<Values extends string[], Context extends Types.TProperties, Result extends Types.TSchema[] = []> = (
|
||||
Values extends [infer Ref extends string, ...infer Rest extends string[]]
|
||||
? HeritageListReduce<Rest, Context, [...Result, Deref<Context, Ref>]>
|
||||
: Result
|
||||
)
|
||||
// prettier-ignore
|
||||
interface HeritageListMapping extends Static.IMapping {
|
||||
output: (
|
||||
this['context'] extends Types.TProperties ?
|
||||
this['input'] extends string[]
|
||||
? HeritageListReduce<this['input'], this['context']>
|
||||
: []
|
||||
: []
|
||||
)
|
||||
}
|
||||
// prettier-ignore
|
||||
type HeritageList = Static.Union<[Delimit<Static.Ident, HeritageListDelimiter>], HeritageListMapping>
|
||||
// ------------------------------------------------------------------
|
||||
// Heritage
|
||||
// ------------------------------------------------------------------
|
||||
// prettier-ignore
|
||||
interface HeritageMapping extends Static.IMapping {
|
||||
output: this['input'] extends ['extends', infer List extends Types.TSchema[]] ? List : []
|
||||
}
|
||||
// prettier-ignore
|
||||
type Heritage = Static.Union<[
|
||||
Static.Tuple<[Static.Const<'extends'>, HeritageList]>,
|
||||
Static.Tuple<[]>
|
||||
], HeritageMapping>
|
||||
// ------------------------------------------------------------------
|
||||
// InterfaceDeclaration
|
||||
// ------------------------------------------------------------------
|
||||
// prettier-ignore
|
||||
interface InterfaceDeclarationMapping extends Static.IMapping {
|
||||
output: this['input'] extends [boolean, 'interface', infer Ident extends string, infer Heritage extends Types.TSchema[], LBrace, infer Properties extends Types.TProperties, RBrace]
|
||||
? { [_ in Ident]: Types.TIntersectEvaluated<[...Heritage, Types.TObject<Properties>]> }
|
||||
: never
|
||||
}
|
||||
// prettier-ignore
|
||||
type InterfaceDeclaration = Static.Tuple<[
|
||||
ExportModifier,
|
||||
Static.Const<'interface'>,
|
||||
Static.Ident,
|
||||
Heritage,
|
||||
Static.Const<LBrace>,
|
||||
Properties,
|
||||
Static.Const<RBrace>,
|
||||
], InterfaceDeclarationMapping>
|
||||
// ------------------------------------------------------------------
|
||||
// ModuleType
|
||||
// ------------------------------------------------------------------
|
||||
// prettier-ignore
|
||||
type ModuleType = Static.Union<[
|
||||
InterfaceDeclaration,
|
||||
TypeAliasDeclaration
|
||||
]>
|
||||
// ------------------------------------------------------------------
|
||||
// ModuleProperties
|
||||
// ------------------------------------------------------------------
|
||||
// prettier-ignore
|
||||
type ModulePropertiesDelimiter = Static.Union<[
|
||||
Static.Tuple<[Static.Const<SemiColon>, Static.Const<Newline>]>,
|
||||
Static.Tuple<[Static.Const<SemiColon>]>,
|
||||
Static.Tuple<[Static.Const<Newline>]>,
|
||||
]>
|
||||
// prettier-ignore
|
||||
type ModulePropertiesReduce<Value extends unknown[], Result extends Types.TProperties = {}> = (
|
||||
Value extends [infer ModuleType extends Types.TProperties, unknown[], ...infer Rest extends unknown[]] ? ModulePropertiesReduce<Rest, Result & ModuleType> :
|
||||
Value extends [infer ModuleType extends Types.TProperties] ? ModulePropertiesReduce<[], Result & ModuleType> :
|
||||
Types.Evaluate<Result>
|
||||
)
|
||||
// prettier-ignore
|
||||
interface ModulePropertiesMapping extends Static.IMapping {
|
||||
output: this['input'] extends unknown[] ? ModulePropertiesReduce<this['input']> : never
|
||||
}
|
||||
// prettier-ignore
|
||||
type ModuleProperties = Static.Union<[
|
||||
Static.Tuple<[ModuleType, ModulePropertiesDelimiter, ModuleProperties]>,
|
||||
Static.Tuple<[ModuleType]>,
|
||||
Static.Tuple<[]>,
|
||||
], ModulePropertiesMapping>
|
||||
// ------------------------------------------------------------------
|
||||
// ModuleDeclaration
|
||||
// ------------------------------------------------------------------
|
||||
// prettier-ignore
|
||||
type ModuleIdentifier = Static.Union<[
|
||||
Static.Tuple<[Static.Ident]>,
|
||||
Static.Tuple<[]>
|
||||
]>
|
||||
// prettier-ignore
|
||||
interface ModuleDeclarationMapping extends Static.IMapping {
|
||||
output: this['input'] extends [boolean, 'module', infer _Ident extends string[], LBrace, infer Properties extends Types.TProperties, RBrace]
|
||||
? Types.TModule<Properties>
|
||||
: never
|
||||
}
|
||||
// prettier-ignore
|
||||
type ModuleDeclaration = Static.Tuple<[
|
||||
ExportModifier, Static.Const<'module'>, ModuleIdentifier, Static.Const<LBrace>, ModuleProperties, Static.Const<RBrace>
|
||||
], ModuleDeclarationMapping>
|
||||
// ------------------------------------------------------------------
|
||||
// Reference
|
||||
// ------------------------------------------------------------------
|
||||
// prettier-ignore
|
||||
interface ReferenceMapping extends Static.IMapping {
|
||||
output: this['input'] extends [infer Key extends string]
|
||||
? Key extends keyof this['context']
|
||||
? this['context'][Key]
|
||||
: Types.TRef<Types.TUnknown>
|
||||
: never
|
||||
output: this['context'] extends Types.TProperties
|
||||
? this['input'] extends string
|
||||
? Deref<this['context'], this['input']>
|
||||
: never
|
||||
: never
|
||||
}
|
||||
type Reference = Static.Tuple<[Static.Ident], ReferenceMapping>
|
||||
|
||||
type Reference = Static.Ident<ReferenceMapping>
|
||||
// ------------------------------------------------------------------
|
||||
// Literal
|
||||
// ------------------------------------------------------------------
|
||||
@@ -198,7 +337,6 @@ type KeyOf = Static.Union<[
|
||||
Static.Tuple<[Static.Const<'keyof'>]>,
|
||||
Static.Tuple<[]>
|
||||
], KeyOfMapping>
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// IndexArray
|
||||
// ------------------------------------------------------------------
|
||||
@@ -231,7 +369,6 @@ type Extends = Static.Union<[
|
||||
Static.Tuple<[Static.Const<'extends'>, Type, Static.Const<Question>, Type, Static.Const<Colon>, Type]>,
|
||||
Static.Tuple<[]>
|
||||
], ExtendsMapping>
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Base
|
||||
// ------------------------------------------------------------------
|
||||
@@ -361,7 +498,7 @@ type Expr = Static.Tuple<[
|
||||
// ------------------------------------------------------------------
|
||||
// Type
|
||||
// ------------------------------------------------------------------
|
||||
export type Type = Expr
|
||||
type Type = Expr
|
||||
// ------------------------------------------------------------------
|
||||
// Properties
|
||||
// ------------------------------------------------------------------
|
||||
@@ -370,34 +507,30 @@ interface PropertyKeyStringMapping extends Static.IMapping {
|
||||
output: this['input']
|
||||
}
|
||||
type PropertyKeyString = Static.String<[SingleQuote, DoubleQuote], PropertyKeyStringMapping>
|
||||
|
||||
type PropertyKey = Static.Union<[Static.Ident, PropertyKeyString]>
|
||||
// prettier-ignore
|
||||
interface PropertyReadonlyMapping extends Static.IMapping {
|
||||
interface ReadonlyMapping extends Static.IMapping {
|
||||
output: this['input'] extends ['readonly'] ? true : false
|
||||
}
|
||||
type PropertyReadonly = Static.Union<[Static.Tuple<[Static.Const<'readonly'>]>, Static.Tuple<[]>], PropertyReadonlyMapping>
|
||||
type Readonly = Static.Union<[Static.Tuple<[Static.Const<'readonly'>]>, Static.Tuple<[]>], ReadonlyMapping>
|
||||
// prettier-ignore
|
||||
interface PropertyOptionalMapping extends Static.IMapping {
|
||||
interface OptionalMapping extends Static.IMapping {
|
||||
output: this['input'] extends [Question] ? true : false
|
||||
}
|
||||
type PropertyOptional = Static.Union<[Static.Tuple<[Static.Const<Question>]>, Static.Tuple<[]>], PropertyOptionalMapping>
|
||||
type Optional = Static.Union<[Static.Tuple<[Static.Const<Question>]>, Static.Tuple<[]>], OptionalMapping>
|
||||
// prettier-ignore
|
||||
interface PropertyMapping extends Static.IMapping {
|
||||
output: this['input'] extends [infer Readonly extends boolean, infer Key extends string, infer Optional extends boolean, string, infer Type extends Types.TSchema]
|
||||
output: this['input'] extends [infer IsReadonly extends boolean, infer Key extends string, infer IsOptional extends boolean, string, infer Type extends Types.TSchema]
|
||||
? {
|
||||
[_ in Key]: (
|
||||
[Readonly, Optional] extends [true, true] ? Types.TReadonlyOptional<Type> :
|
||||
[Readonly, Optional] extends [true, false] ? Types.TReadonly<Type> :
|
||||
[Readonly, Optional] extends [false, true] ? Types.TOptional<Type> :
|
||||
[IsReadonly, IsOptional] extends [true, true] ? Types.TReadonlyOptional<Type> :
|
||||
[IsReadonly, IsOptional] extends [true, false] ? Types.TReadonly<Type> :
|
||||
[IsReadonly, IsOptional] extends [false, true] ? Types.TOptional<Type> :
|
||||
Type
|
||||
)
|
||||
} : never
|
||||
}
|
||||
|
||||
type Property = Static.Tuple<[PropertyReadonly, PropertyKey, PropertyOptional, Static.Const<Colon>, Type], PropertyMapping>
|
||||
|
||||
type PropertiesEvaluate<T> = { [K in keyof T]: T[K] } & {}
|
||||
type Property = Static.Tuple<[Readonly, PropertyKey, Optional, Static.Const<Colon>, Type], PropertyMapping>
|
||||
// prettier-ignore
|
||||
type PropertyDelimiter = Static.Union<[
|
||||
Static.Tuple<[Static.Const<Comma>, Static.Const<Newline>]>,
|
||||
@@ -409,7 +542,7 @@ type PropertyDelimiter = Static.Union<[
|
||||
// prettier-ignore
|
||||
type PropertiesReduce<PropertiesArray extends Types.TProperties[], Result extends Types.TProperties = {}> = (
|
||||
PropertiesArray extends [infer Left extends Types.TProperties, ...infer Right extends Types.TProperties[]]
|
||||
? PropertiesReduce<Right, PropertiesEvaluate<Result & Left>>
|
||||
? PropertiesReduce<Right, Types.Evaluate<Result & Left>>
|
||||
: Result
|
||||
)
|
||||
// prettier-ignore
|
||||
@@ -485,7 +618,7 @@ type Constructor = Static.Tuple<[
|
||||
// ------------------------------------------------------------------
|
||||
// prettier-ignore
|
||||
interface MappedMapping extends Static.IMapping {
|
||||
output: this['input'] extends [LBrace, LBracket, infer Key extends string, 'in', infer Right extends Types.TSchema, RBracket, Colon, infer Type extends Types.TSchema, RBrace]
|
||||
output: this['input'] extends [LBrace, LBracket, infer _Key extends string, 'in', infer _Right extends Types.TSchema, RBracket, Colon, infer Type extends Types.TSchema, RBrace]
|
||||
? Types.TLiteral<'Mapped types not supported'>
|
||||
: this['input']
|
||||
}
|
||||
@@ -762,3 +895,14 @@ type Date = Static.Const<'Date', Static.As<Types.TDate>>
|
||||
// Uint8Array
|
||||
// ------------------------------------------------------------------
|
||||
type Uint8Array = Static.Const<'Uint8Array', Static.As<Types.TUint8Array>>
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Main
|
||||
// ------------------------------------------------------------------
|
||||
// prettier-ignore
|
||||
export type Main = Static.Union<[
|
||||
ModuleDeclaration,
|
||||
TypeAliasDeclaration,
|
||||
InterfaceDeclaration,
|
||||
Type
|
||||
]>
|
||||
|
||||
@@ -1,174 +0,0 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
@sinclair/typebox/type
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2024 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
import type { TSchema } from '../schema/index'
|
||||
import type { Evaluate } from '../helpers/index'
|
||||
import type { TTuple } from '../tuple/index'
|
||||
import type { TIntersect } from '../intersect/index'
|
||||
import type { TUnion } from '../union/index'
|
||||
import type { TPromise } from '../promise/index'
|
||||
import type { TAsyncIterator } from '../async-iterator/index'
|
||||
import type { TIterator } from '../iterator/index'
|
||||
import type { TArray } from '../array/index'
|
||||
import type { TConstructor } from '../constructor/index'
|
||||
import type { TFunction } from '../function/index'
|
||||
import type { TRef } from '../ref/index'
|
||||
import type { TObject, TProperties } from '../object/index'
|
||||
import { CloneType, CloneRest } from '../clone/type'
|
||||
import { Discard } from '../discard/index'
|
||||
import { IsUndefined } from '../guard/value'
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// TypeGuard
|
||||
// ------------------------------------------------------------------
|
||||
import { IsConstructor, IsFunction, IsIntersect, IsUnion, IsTuple, IsArray, IsObject, IsPromise, IsAsyncIterator, IsIterator, IsRef } from '../guard/kind'
|
||||
// ------------------------------------------------------------------
|
||||
// FromRest
|
||||
// ------------------------------------------------------------------
|
||||
// prettier-ignore
|
||||
export type TFromRest<T extends TSchema[], Acc extends TSchema[] = []> = (
|
||||
T extends [infer L extends TSchema, ...infer R extends TSchema[]]
|
||||
? TFromRest<R, [...Acc, TDeref<L>]>
|
||||
: Acc
|
||||
)
|
||||
function FromRest<T extends TSchema[]>(schema: [...T], references: TSchema[]): TFromRest<T> {
|
||||
return schema.map((schema) => Deref(schema, references)) as never
|
||||
}
|
||||
// ------------------------------------------------------------------
|
||||
// FromProperties
|
||||
// ------------------------------------------------------------------
|
||||
// prettier-ignore
|
||||
type FromProperties<T extends TProperties> = Evaluate<{
|
||||
[K in keyof T]: TDeref<T[K]>
|
||||
}>
|
||||
// prettier-ignore
|
||||
function FromProperties(properties: TProperties, references: TSchema[]) {
|
||||
const Acc = {} as TProperties
|
||||
for(const K of globalThis.Object.getOwnPropertyNames(properties)) {
|
||||
Acc[K] = Deref(properties[K], references)
|
||||
}
|
||||
return Acc as never
|
||||
}
|
||||
// prettier-ignore
|
||||
function FromConstructor(schema: TConstructor, references: TSchema[]) {
|
||||
schema.parameters = FromRest(schema.parameters, references)
|
||||
schema.returns = Deref(schema.returns, references)
|
||||
return schema
|
||||
}
|
||||
// prettier-ignore
|
||||
function FromFunction(schema: TFunction, references: TSchema[]) {
|
||||
schema.parameters = FromRest(schema.parameters, references)
|
||||
schema.returns = Deref(schema.returns, references)
|
||||
return schema
|
||||
}
|
||||
// prettier-ignore
|
||||
function FromIntersect(schema: TIntersect, references: TSchema[]) {
|
||||
schema.allOf = FromRest(schema.allOf, references)
|
||||
return schema
|
||||
}
|
||||
// prettier-ignore
|
||||
function FromUnion(schema: TUnion, references: TSchema[]) {
|
||||
schema.anyOf = FromRest(schema.anyOf, references)
|
||||
return schema
|
||||
}
|
||||
// prettier-ignore
|
||||
function FromTuple(schema: TTuple, references: TSchema[]) {
|
||||
if(IsUndefined(schema.items)) return schema
|
||||
schema.items = FromRest(schema.items, references)
|
||||
return schema
|
||||
}
|
||||
// prettier-ignore
|
||||
function FromArray(schema: TArray, references: TSchema[]) {
|
||||
schema.items = Deref(schema.items, references)
|
||||
return schema
|
||||
}
|
||||
// prettier-ignore
|
||||
function FromObject(schema: TObject, references: TSchema[]) {
|
||||
schema.properties = FromProperties(schema.properties, references)
|
||||
return schema
|
||||
}
|
||||
// prettier-ignore
|
||||
function FromPromise(schema: TPromise, references: TSchema[]) {
|
||||
schema.item = Deref(schema.item, references)
|
||||
return schema
|
||||
}
|
||||
// prettier-ignore
|
||||
function FromAsyncIterator(schema: TAsyncIterator, references: TSchema[]) {
|
||||
schema.items = Deref(schema.items, references)
|
||||
return schema
|
||||
}
|
||||
// prettier-ignore
|
||||
function FromIterator(schema: TIterator, references: TSchema[]) {
|
||||
schema.items = Deref(schema.items, references)
|
||||
return schema
|
||||
}
|
||||
// prettier-ignore
|
||||
function FromRef(schema: TRef, references: TSchema[]) {
|
||||
const target = references.find(remote => remote.$id === schema.$ref)
|
||||
if(target === undefined) throw Error(`Unable to dereference schema with $id ${schema.$ref}`)
|
||||
const discard = Discard(target, ['$id']) as TSchema
|
||||
return Deref(discard, references)
|
||||
}
|
||||
// prettier-ignore
|
||||
function DerefResolve<T extends TSchema>(schema: T, references: TSchema[]): TDeref<T> {
|
||||
return (
|
||||
IsConstructor(schema) ? FromConstructor(schema, references) :
|
||||
IsFunction(schema) ? FromFunction(schema, references) :
|
||||
IsIntersect(schema) ? FromIntersect(schema, references) :
|
||||
IsUnion(schema) ? FromUnion(schema, references) :
|
||||
IsTuple(schema) ? FromTuple(schema, references) :
|
||||
IsArray(schema) ? FromArray(schema, references) :
|
||||
IsObject(schema) ? FromObject(schema, references) :
|
||||
IsPromise(schema) ? FromPromise(schema, references) :
|
||||
IsAsyncIterator(schema) ? FromAsyncIterator(schema, references) :
|
||||
IsIterator(schema) ? FromIterator(schema, references) :
|
||||
IsRef(schema) ? FromRef(schema, references) :
|
||||
schema
|
||||
) as never
|
||||
}
|
||||
// prettier-ignore
|
||||
export type TDeref<T extends TSchema> =
|
||||
T extends TConstructor<infer S extends TSchema[], infer R extends TSchema> ? TConstructor<TFromRest<S>, TDeref<R>> :
|
||||
T extends TFunction<infer S extends TSchema[], infer R extends TSchema> ? TFunction<TFromRest<S>, TDeref<R>> :
|
||||
T extends TIntersect<infer S extends TSchema[]> ? TIntersect<TFromRest<S>> :
|
||||
T extends TUnion<infer S extends TSchema[]> ? TUnion<TFromRest<S>> :
|
||||
T extends TTuple<infer S extends TSchema[]> ? TTuple<TFromRest<S>> :
|
||||
T extends TObject<infer S extends TProperties> ? TObject<FromProperties<S>> :
|
||||
T extends TArray<infer S extends TSchema> ? TArray<TDeref<S>> :
|
||||
T extends TPromise<infer S extends TSchema> ? TPromise<TDeref<S>> :
|
||||
T extends TAsyncIterator<infer S extends TSchema> ? TAsyncIterator<TDeref<S>> :
|
||||
T extends TIterator<infer S extends TSchema> ? TIterator<TDeref<S>> :
|
||||
T extends TRef<infer S extends TSchema> ? TDeref<S> :
|
||||
T
|
||||
// ------------------------------------------------------------------
|
||||
// TDeref
|
||||
// ------------------------------------------------------------------
|
||||
/** `[Json]` Creates a dereferenced type */
|
||||
export function Deref<T extends TSchema>(schema: T, references: TSchema[]): TDeref<T> {
|
||||
return DerefResolve(CloneType(schema), CloneRest(references))
|
||||
}
|
||||
@@ -40,6 +40,7 @@ import type { TAsyncIterator } from '../async-iterator/index'
|
||||
import type { TBigInt } from '../bigint/index'
|
||||
import type { TConstructor } from '../constructor/index'
|
||||
import type { TFunction } from '../function/index'
|
||||
import type { TImport } from '../module/index'
|
||||
import type { TInteger } from '../integer/index'
|
||||
import type { TIntersect } from '../intersect/index'
|
||||
import type { TIterator } from '../iterator/index'
|
||||
@@ -107,6 +108,10 @@ export function IsFunction(value: unknown): value is TFunction {
|
||||
return IsKindOf(value, 'Function')
|
||||
}
|
||||
/** `[Kind-Only]` Returns true if the given value is TInteger */
|
||||
export function IsImport(value: unknown): value is TImport {
|
||||
return IsKindOf(value, 'Import')
|
||||
}
|
||||
/** `[Kind-Only]` Returns true if the given value is TInteger */
|
||||
export function IsInteger(value: unknown): value is TInteger {
|
||||
return IsKindOf(value, 'Integer')
|
||||
}
|
||||
|
||||
@@ -41,6 +41,7 @@ import type { TAsyncIterator } from '../async-iterator/index'
|
||||
import type { TBigInt } from '../bigint/index'
|
||||
import type { TConstructor } from '../constructor/index'
|
||||
import type { TFunction } from '../function/index'
|
||||
import type { TImport } from '../module/index'
|
||||
import type { TInteger } from '../integer/index'
|
||||
import type { TIntersect } from '../intersect/index'
|
||||
import type { TIterator } from '../iterator/index'
|
||||
@@ -253,6 +254,19 @@ export function IsFunction(value: unknown): value is TFunction {
|
||||
IsSchema(value.returns)
|
||||
)
|
||||
}
|
||||
/** Returns true if the given value is TImport */
|
||||
export function IsImport(value: unknown): value is TImport {
|
||||
// prettier-ignore
|
||||
return (
|
||||
IsKindOf(value, 'Import') &&
|
||||
ValueGuard.HasPropertyKey(value, '$defs') &&
|
||||
ValueGuard.IsObject(value.$defs) &&
|
||||
IsProperties(value.$defs) &&
|
||||
ValueGuard.HasPropertyKey(value, '$ref') &&
|
||||
ValueGuard.IsString(value.$ref) &&
|
||||
value.$ref in value.$defs // required
|
||||
)
|
||||
}
|
||||
/** Returns true if the given value is TInteger */
|
||||
export function IsInteger(value: unknown): value is TInteger {
|
||||
return (
|
||||
|
||||
@@ -38,7 +38,6 @@ export * from './const/index'
|
||||
export * from './constructor/index'
|
||||
export * from './constructor-parameters/index'
|
||||
export * from './date/index'
|
||||
export * from './deref/index'
|
||||
export * from './discard/index'
|
||||
export * from './enum/index'
|
||||
export * from './error/index'
|
||||
@@ -57,6 +56,7 @@ export * from './iterator/index'
|
||||
export * from './keyof/index'
|
||||
export * from './literal/index'
|
||||
export * from './mapped/index'
|
||||
export * from './module/index'
|
||||
export * from './never/index'
|
||||
export * from './not/index'
|
||||
export * from './null/index'
|
||||
@@ -82,7 +82,6 @@ export * from './return-type/index'
|
||||
export * from './schema/index'
|
||||
export * from './sets/index'
|
||||
export * from './static/index'
|
||||
export * from './strict/index'
|
||||
export * from './string/index'
|
||||
export * from './symbol/index'
|
||||
export * from './symbols/index'
|
||||
|
||||
@@ -26,4 +26,4 @@ THE SOFTWARE.
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
export * from './deref'
|
||||
export * from './module'
|
||||
189
src/type/module/module.ts
Normal file
189
src/type/module/module.ts
Normal file
@@ -0,0 +1,189 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
@sinclair/typebox/type
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2024 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
import { Ensure } from '../helpers/index'
|
||||
import { CreateType } from '../create/index'
|
||||
import { Kind } from '../symbols/index'
|
||||
import { SchemaOptions, TSchema } from '../schema/index'
|
||||
import { TObject, TProperties } from '../object/index'
|
||||
import { TConstructor } from '../constructor/index'
|
||||
import { TFunction } from '../function/index'
|
||||
import { TTuple } from '../tuple/index'
|
||||
import { TIntersect } from '../intersect/index'
|
||||
import { TUnion } from '../union/index'
|
||||
import { TArray } from '../array/index'
|
||||
import { TAsyncIterator } from '../async-iterator/index'
|
||||
import { TIterator } from '../iterator/index'
|
||||
import { TLiteral, TLiteralValue } from '../literal/index'
|
||||
import { TAny } from '../any/index'
|
||||
import { TBigInt } from '../bigint/index'
|
||||
import { TBoolean } from '../boolean/index'
|
||||
import { TDate } from '../date/index'
|
||||
import { TInteger } from '../integer/index'
|
||||
import { TNever } from '../never/index'
|
||||
import { TNumber } from '../number/index'
|
||||
import { TNull } from '../null/index'
|
||||
import { TRef } from '../ref/index'
|
||||
import { TRegExp } from '../regexp/index'
|
||||
import { TString } from '../string/index'
|
||||
import { TSymbol } from '../symbol/index'
|
||||
import { TTemplateLiteral, TTemplateLiteralKind } from '../template-literal/index'
|
||||
import { TUint8Array } from '../uint8array/index'
|
||||
import { TUndefined } from '../undefined/index'
|
||||
import { TUnknown } from '../unknown/index'
|
||||
import { TVoid } from '../void/index'
|
||||
import { Static } from '../static/index'
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// Infer
|
||||
// ------------------------------------------------------------------
|
||||
// prettier-ignore
|
||||
type InferImport<Properties extends TProperties, Key extends keyof Properties, ModuleProperties extends TProperties> = (
|
||||
Infer<Properties[Key], ModuleProperties>
|
||||
)
|
||||
// prettier-ignore
|
||||
type InferRef<Ref extends string, Properties extends TProperties> = (
|
||||
Ref extends keyof Properties ? Infer<Properties[Ref], Properties> : never
|
||||
)
|
||||
// prettier-ignore
|
||||
type InferObject<Properties extends TProperties, ModuleProperties extends TProperties> = {
|
||||
[K in keyof Properties]: Infer<Properties[K], ModuleProperties>
|
||||
} & {}
|
||||
// prettier-ignore
|
||||
type InferConstructor<Parameters extends TSchema[], InstanceType extends TSchema, Properties extends TProperties> = Ensure<
|
||||
new (...args: InferTuple<Parameters, Properties>) => Infer<InstanceType, Properties>
|
||||
>
|
||||
// prettier-ignore
|
||||
type InferFunction<Parameters extends TSchema[], ReturnType extends TSchema, Properties extends TProperties> = Ensure<
|
||||
(...args: InferTuple<Parameters, Properties>) => Infer<ReturnType, Properties>
|
||||
>
|
||||
// prettier-ignore
|
||||
type InferTuple<Types extends TSchema[], Properties extends TProperties, Result extends unknown[] = []> = (
|
||||
Types extends [infer L extends TSchema, ...infer R extends TSchema[]]
|
||||
? InferTuple<R, Properties, [...Result, Infer<L, Properties>]>
|
||||
: Result
|
||||
)
|
||||
// prettier-ignore
|
||||
type InferIntersect<Types extends TSchema[], Properties extends TProperties, Result extends unknown = unknown> = (
|
||||
Types extends [infer L extends TSchema, ...infer R extends TSchema[]]
|
||||
? InferIntersect<R, Properties, Result & Infer<L, Properties>>
|
||||
: Result
|
||||
)
|
||||
// prettier-ignore
|
||||
type InferUnion<Types extends TSchema[], Properties extends TProperties, Result extends unknown = never> = (
|
||||
Types extends [infer L extends TSchema, ...infer R extends TSchema[]]
|
||||
? InferUnion<R, Properties, Result | Infer<L, Properties>>
|
||||
: Result
|
||||
)
|
||||
// prettier-ignore
|
||||
type InferArray<Type extends TSchema, Module extends TProperties> = (
|
||||
Ensure<Array<Infer<Type, Module>>>
|
||||
)
|
||||
// prettier-ignore
|
||||
type InferAsyncIterator<Type extends TSchema, Properties extends TProperties> = (
|
||||
Ensure<AsyncIterableIterator<Infer<Type, Properties>>>
|
||||
)
|
||||
// prettier-ignore
|
||||
type InferIterator<Type extends TSchema, Properties extends TProperties> = (
|
||||
Ensure<IterableIterator<Infer<Type, Properties>>>
|
||||
)
|
||||
// prettier-ignore
|
||||
type Infer<Type extends TSchema, Properties extends TProperties = {}> = (
|
||||
Type extends TImport<infer S extends TProperties, infer K extends string> ? InferImport<S, K, S> :
|
||||
Type extends TRef<infer S extends string> ? InferRef<S, Properties> :
|
||||
Type extends TObject<infer S extends TProperties> ? InferObject<S, Properties> :
|
||||
Type extends TConstructor<infer S extends TSchema[], infer R extends TSchema> ? InferConstructor<S, R, Properties> :
|
||||
Type extends TFunction<infer S extends TSchema[], infer R extends TSchema> ? InferFunction<S, R, Properties> :
|
||||
Type extends TTuple<infer S extends TSchema[]> ? InferTuple<S, Properties> :
|
||||
Type extends TIntersect<infer S extends TSchema[]> ? InferIntersect<S, Properties> :
|
||||
Type extends TUnion<infer S extends TSchema[]> ? InferUnion<S, Properties> :
|
||||
Type extends TArray<infer S extends TSchema> ? InferArray<S, Properties> :
|
||||
Type extends TAsyncIterator<infer S extends TSchema> ? InferAsyncIterator<S, Properties> :
|
||||
Type extends TIterator<infer S extends TSchema> ? InferIterator<S, Properties> :
|
||||
Type extends TTemplateLiteral<infer S extends TTemplateLiteralKind[]> ? Static<TTemplateLiteral<S>> :
|
||||
Type extends TLiteral<infer S extends TLiteralValue> ? S :
|
||||
Type extends TAny ? any :
|
||||
Type extends TBigInt ? bigint :
|
||||
Type extends TBoolean ? boolean :
|
||||
Type extends TDate ? Date :
|
||||
Type extends TInteger ? number :
|
||||
Type extends TNever ? never :
|
||||
Type extends TNumber ? number :
|
||||
Type extends TRegExp ? string :
|
||||
Type extends TString ? string :
|
||||
Type extends TSymbol ? symbol :
|
||||
Type extends TNull ? null :
|
||||
Type extends TUint8Array ? Uint8Array :
|
||||
Type extends TUndefined ? undefined :
|
||||
Type extends TUnknown ? unknown :
|
||||
Type extends TVoid ? void :
|
||||
never
|
||||
)
|
||||
// ------------------------------------------------------------------
|
||||
// Definitions
|
||||
// ------------------------------------------------------------------
|
||||
export interface TDefinitions<ModuleProperties extends TProperties> extends TSchema {
|
||||
static: { [K in keyof ModuleProperties]: Static<ModuleProperties[K]> }
|
||||
$defs: ModuleProperties
|
||||
}
|
||||
// ------------------------------------------------------------------
|
||||
// Import
|
||||
// ------------------------------------------------------------------
|
||||
// prettier-ignore
|
||||
export interface TImport<ModuleProperties extends TProperties = {}, Key extends keyof ModuleProperties = keyof ModuleProperties> extends TSchema {
|
||||
[Kind]: 'Import'
|
||||
static: InferImport<ModuleProperties, Key, ModuleProperties>
|
||||
$defs: ModuleProperties
|
||||
$ref: Key
|
||||
}
|
||||
// ------------------------------------------------------------------
|
||||
// Module
|
||||
// ------------------------------------------------------------------
|
||||
// prettier-ignore
|
||||
export class TModule<Properties extends TProperties> {
|
||||
constructor(private readonly $defs: Properties, private readonly options: SchemaOptions = {}) {}
|
||||
|
||||
/** `[Json]` Returns the Type definitions for this module */
|
||||
public Defs(): TDefinitions<Properties> {
|
||||
return CreateType({ $defs: this.ResolveDefinitionsWithIdentifiers() }, this.options) as never
|
||||
}
|
||||
/** `[Json]` Imports a Type by Key. */
|
||||
public Import<Key extends keyof Properties>(key: Key, options?: SchemaOptions): TImport<Properties, Key> {
|
||||
return CreateType({ [Kind]: 'Import', $defs: this.ResolveDefinitionsWithIdentifiers(), $ref: key }, options) as never
|
||||
}
|
||||
/** `[Internal]` For each definition, assign an `$id` property. */
|
||||
private ResolveDefinitionsWithIdentifiers() {
|
||||
return globalThis.Object.getOwnPropertyNames(this.$defs).reduce((Result, Key) => (
|
||||
{ ...Result, [Key]: { ...this.$defs[Key], $id: Key }}
|
||||
), {})
|
||||
}
|
||||
}
|
||||
/** `[Json]` Creates a Type Definition Module. */
|
||||
export function Module<Properties extends TProperties>(properties: Properties): TModule<Properties> {
|
||||
return new TModule(properties)
|
||||
}
|
||||
@@ -26,29 +26,19 @@ THE SOFTWARE.
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
import { CreateType } from '../create/type'
|
||||
import type { TSchema, SchemaOptions } from '../schema/index'
|
||||
import type { Static } from '../static/index'
|
||||
import { CreateType } from '../create/type'
|
||||
import { Kind } from '../symbols/index'
|
||||
// ------------------------------------------------------------------
|
||||
// ValueGuard
|
||||
// ------------------------------------------------------------------
|
||||
import { IsString, IsUndefined } from '../guard/value'
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// TRef
|
||||
// ------------------------------------------------------------------
|
||||
export interface TRef<T extends TSchema = TSchema> extends TSchema {
|
||||
export interface TRef<Ref extends string = string> extends TSchema {
|
||||
[Kind]: 'Ref'
|
||||
static: Static<T, this['params']>
|
||||
$ref: string
|
||||
static: unknown
|
||||
$ref: Ref
|
||||
}
|
||||
/** `[Json]` Creates a Ref type. The referenced type must contain a $id */
|
||||
export function Ref<T extends TSchema>(schema: T, options?: SchemaOptions): TRef<T>
|
||||
/** `[Json]` Creates a Ref type. */
|
||||
export function Ref<T extends TSchema>($ref: string, options?: SchemaOptions): TRef<T>
|
||||
/** `[Json]` Creates a Ref type. */
|
||||
export function Ref(unresolved: TSchema | string, options?: SchemaOptions) {
|
||||
if (IsString(unresolved)) return CreateType({ [Kind]: 'Ref', $ref: unresolved }, options)
|
||||
if (IsUndefined(unresolved.$id)) throw new Error('Reference target type must specify an $id')
|
||||
return CreateType({ [Kind]: 'Ref', $ref: unresolved.$id! }, options)
|
||||
export function Ref<Ref extends string>($ref: Ref, options?: SchemaOptions): TRef<Ref> {
|
||||
return CreateType({ [Kind]: 'Ref', $ref }, options) as never
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ export type TDecodeType<T extends TSchema> = (
|
||||
T extends TPromise<infer S extends TSchema> ? TPromise<TDecodeType<S>> :
|
||||
T extends TRecord<infer K, infer S> ? TRecord<K, TDecodeType<S>> :
|
||||
T extends TRecursive<infer S extends TSchema> ? TRecursive<TDecodeType<S>> :
|
||||
T extends TRef<infer S extends TSchema> ? TRef<TDecodeType<S>> :
|
||||
T extends TRef<infer S extends string> ? TRef<S> :
|
||||
T extends TTuple<infer S extends TSchema[]> ? TTuple<TDecodeRest<S>> :
|
||||
T extends TUnion<infer S extends TSchema[]> ? TUnion<TDecodeRest<S>> :
|
||||
T
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
@sinclair/typebox/type
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2024 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 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'
|
||||
@@ -1,44 +0,0 @@
|
||||
/*--------------------------------------------------------------------------
|
||||
|
||||
@sinclair/typebox/type
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2017-2024 Haydn Paterson (sinclair) <haydn.developer@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
import type { TSchema } from '../schema/index'
|
||||
|
||||
export type TStrict<T extends TSchema> = T
|
||||
|
||||
/**
|
||||
* @deprecated `[Json]` Omits compositing symbols from this schema. It is recommended
|
||||
* to use the JSON parse/stringify to remove compositing symbols if needed. This
|
||||
* is how Strict works internally.
|
||||
*
|
||||
* ```typescript
|
||||
* JSON.parse(JSON.stringify(Type.String()))
|
||||
* ```
|
||||
*/
|
||||
export function Strict<T extends TSchema>(schema: T): TStrict<T> {
|
||||
return JSON.parse(JSON.stringify(schema))
|
||||
}
|
||||
@@ -31,7 +31,6 @@ import { Array, type TArray, type ArrayOptions } from '../array/index'
|
||||
import { Boolean, type TBoolean } from '../boolean/index'
|
||||
import { Composite, type TComposite } from '../composite/index'
|
||||
import { Const, type TConst } from '../const/index'
|
||||
import { Deref, type TDeref } from '../deref/index'
|
||||
import { Enum, type TEnum, type TEnumKey, type TEnumValue } from '../enum/index'
|
||||
import { Exclude, type TExclude, type TExcludeFromMappedResult, type TExcludeFromTemplateLiteral } from '../exclude/index'
|
||||
import { Extends, type TExtends, type TExtendsFromMappedKey, type TExtendsFromMappedResult } from '../extends/index'
|
||||
@@ -47,6 +46,7 @@ import { Never, type TNever } from '../never/index'
|
||||
import { Not, type TNot } from '../not/index'
|
||||
import { Null, type TNull } from '../null/index'
|
||||
import { type TMappedKey } from '../mapped/index'
|
||||
import { Module, TModule } from '../module/index'
|
||||
import { Number, type TNumber, type NumberOptions } from '../number/index'
|
||||
import { Object, type TObject, type TProperties, type ObjectOptions } from '../object/index'
|
||||
import { Omit, type TOmit, type TOmitFromMappedKey, type TOmitFromMappedResult } from '../omit/index'
|
||||
@@ -61,7 +61,6 @@ import { Ref, type TRef } from '../ref/index'
|
||||
import { Required, type TRequired, type TRequiredFromMappedResult } from '../required/index'
|
||||
import { Rest, type TRest } from '../rest/index'
|
||||
import { type TSchema, type SchemaOptions } from '../schema/index'
|
||||
import { Strict, type TStrict } from '../strict/index'
|
||||
import { String, type TString, type StringOptions } from '../string/index'
|
||||
import { TemplateLiteral, type TTemplateLiteral, type TTemplateLiteralKind, type TTemplateLiteralSyntax } from '../template-literal/index'
|
||||
import { Transform, TransformDecodeBuilder } from '../transform/index'
|
||||
@@ -72,21 +71,6 @@ import { Unsafe, type TUnsafe, type UnsafeOptions } from '../unsafe/index'
|
||||
|
||||
/** Json Type Builder with Static Resolution for TypeScript */
|
||||
export class JsonTypeBuilder {
|
||||
// ------------------------------------------------------------------------
|
||||
// Strict
|
||||
// ------------------------------------------------------------------------
|
||||
/**
|
||||
* @deprecated `[Json]` Omits compositing symbols from this schema. It is recommended
|
||||
* to use the JSON parse/stringify to remove compositing symbols if needed. This
|
||||
* is how Strict works internally.
|
||||
*
|
||||
* ```typescript
|
||||
* JSON.parse(JSON.stringify(Type.String()))
|
||||
* ```
|
||||
*/
|
||||
public Strict<T extends TSchema>(schema: T): TStrict<T> {
|
||||
return Strict(schema)
|
||||
}
|
||||
// ------------------------------------------------------------------------
|
||||
// Modifiers
|
||||
// ------------------------------------------------------------------------
|
||||
@@ -145,10 +129,6 @@ export class JsonTypeBuilder {
|
||||
public Const</* const (not supported in 4.0) */ T>(value: T, options?: SchemaOptions): TConst<T> {
|
||||
return Const(value, options)
|
||||
}
|
||||
/** `[Json]` Creates a dereferenced type */
|
||||
public Deref<T extends TSchema>(schema: T, references: TSchema[]): TDeref<T> {
|
||||
return Deref(schema, references)
|
||||
}
|
||||
/** `[Json]` Creates a Enum type */
|
||||
public Enum<V extends TEnumValue, T extends Record<TEnumKey, V>>(item: T, options?: SchemaOptions): TEnum<T> {
|
||||
return Enum(item, options)
|
||||
@@ -227,6 +207,10 @@ export class JsonTypeBuilder {
|
||||
public Mapped(key: any, map: TMappedFunction<any>, options?: ObjectOptions): any {
|
||||
return Mapped(key, map, options)
|
||||
}
|
||||
/** `[Json]` Creates a Type Definition Module. */
|
||||
public Module<Properties extends TProperties>(properties: Properties): TModule<Properties> {
|
||||
return Module(properties)
|
||||
}
|
||||
/** `[Json]` Creates a Never type */
|
||||
public Never(options?: SchemaOptions): TNever {
|
||||
return Never(options)
|
||||
@@ -287,13 +271,9 @@ export class JsonTypeBuilder {
|
||||
public Recursive<T extends TSchema>(callback: (thisType: TThis) => T, options?: SchemaOptions): TRecursive<T> {
|
||||
return Recursive(callback, options)
|
||||
}
|
||||
/** `[Json]` Creates a Ref type. The referenced type must contain a $id */
|
||||
public Ref<T extends TSchema>(schema: T, options?: SchemaOptions): TRef<T>
|
||||
/** `[Json]` Creates a Ref type. */
|
||||
public Ref<T extends TSchema>($ref: string, options?: SchemaOptions): TRef<T>
|
||||
/** `[Json]` Creates a Ref type. */
|
||||
public Ref(unresolved: TSchema | string, options?: SchemaOptions) {
|
||||
return Ref(unresolved as any, options)
|
||||
public Ref<Ref extends string>($ref: Ref, options?: SchemaOptions): TRef<Ref> {
|
||||
return Ref($ref, options)
|
||||
}
|
||||
/** `[Json]` Constructs a type where all properties are required */
|
||||
public Required<T extends TMappedResult>(T: T, options?: SchemaOptions): TRequiredFromMappedResult<T>
|
||||
|
||||
@@ -40,7 +40,6 @@ export { Const } from '../const/index'
|
||||
export { Constructor } from '../constructor/index'
|
||||
export { ConstructorParameters } from '../constructor-parameters/index'
|
||||
export { Date } from '../date/index'
|
||||
export { Deref } from '../deref/index'
|
||||
export { Enum } from '../enum/index'
|
||||
export { Exclude } from '../exclude/index'
|
||||
export { Extends } from '../extends/index'
|
||||
@@ -55,6 +54,7 @@ export { Iterator } from '../iterator/index'
|
||||
export { KeyOf } from '../keyof/index'
|
||||
export { Literal } from '../literal/index'
|
||||
export { Mapped } from '../mapped/index'
|
||||
export { Module } from '../module/index'
|
||||
export { Never } from '../never/index'
|
||||
export { Not } from '../not/index'
|
||||
export { Null } from '../null/index'
|
||||
@@ -76,7 +76,6 @@ export { Required } from '../required/index'
|
||||
export { Rest } from '../rest/index'
|
||||
export { ReturnType } from '../return-type/index'
|
||||
export { String } from '../string/index'
|
||||
export { Strict } from '../strict/index'
|
||||
export { Symbol } from '../symbol/index'
|
||||
export { TemplateLiteral } from '../template-literal/index'
|
||||
export { Transform } from '../transform/index'
|
||||
|
||||
@@ -32,12 +32,13 @@ import { Kind } from '../../type/symbols/index'
|
||||
import { Create } from '../create/index'
|
||||
import { Check } from '../check/index'
|
||||
import { Clone } from '../clone/index'
|
||||
import { Deref } from '../deref/index'
|
||||
import { Deref, Pushref } from '../deref/index'
|
||||
|
||||
import type { TSchema } from '../../type/schema/index'
|
||||
import type { Static } from '../../type/static/index'
|
||||
import type { TArray } from '../../type/array/index'
|
||||
import type { TConstructor } from '../../type/constructor/index'
|
||||
import type { TImport } from '../../type/module/index'
|
||||
import type { TIntersect } from '../../type/intersect/index'
|
||||
import type { TObject } from '../../type/object/index'
|
||||
import type { TRecord } from '../../type/record/index'
|
||||
@@ -133,6 +134,11 @@ function FromConstructor(schema: TConstructor, references: TSchema[], value: any
|
||||
}
|
||||
return result
|
||||
}
|
||||
function FromImport(schema: TImport, references: TSchema[], value: unknown): boolean {
|
||||
const definitions = globalThis.Object.values(schema.$defs) as TSchema[]
|
||||
const target = schema.$defs[schema.$ref] as TSchema
|
||||
return Visit(target, [...references, ...definitions], value)
|
||||
}
|
||||
function FromIntersect(schema: TIntersect, references: TSchema[], value: any): any {
|
||||
const created = Create(schema, references)
|
||||
const mapped = IsObject(created) && IsObject(value) ? { ...(created as any), ...value } : value
|
||||
@@ -187,7 +193,7 @@ function FromUnion(schema: TUnion, references: TSchema[], value: any): any {
|
||||
return Check(schema, references, value) ? Clone(value) : CastUnion(schema, references, value)
|
||||
}
|
||||
function Visit(schema: TSchema, references: TSchema[], value: any): any {
|
||||
const references_ = IsString(schema.$id) ? [...references, schema] : references
|
||||
const references_ = IsString(schema.$id) ? Pushref(schema, references) : references
|
||||
const schema_ = schema as any
|
||||
switch (schema[Kind]) {
|
||||
// --------------------------------------------------------------
|
||||
@@ -197,6 +203,8 @@ function Visit(schema: TSchema, references: TSchema[], value: any): any {
|
||||
return FromArray(schema_, references_, value)
|
||||
case 'Constructor':
|
||||
return FromConstructor(schema_, references_, value)
|
||||
case 'Import':
|
||||
return FromImport(schema_, references_, value)
|
||||
case 'Intersect':
|
||||
return FromIntersect(schema_, references_, value)
|
||||
case 'Never':
|
||||
|
||||
@@ -27,7 +27,7 @@ THE SOFTWARE.
|
||||
---------------------------------------------------------------------------*/
|
||||
|
||||
import { TypeSystemPolicy } from '../../system/index'
|
||||
import { Deref } from '../deref/index'
|
||||
import { Deref, Pushref } from '../deref/index'
|
||||
import { Hash } from '../hash/index'
|
||||
import { Kind } from '../../type/symbols/index'
|
||||
import { KeyOfPattern } from '../../type/keyof/index'
|
||||
@@ -48,6 +48,7 @@ import type { TInteger } from '../../type/integer/index'
|
||||
import type { TIntersect } from '../../type/intersect/index'
|
||||
import type { TIterator } from '../../type/iterator/index'
|
||||
import type { TLiteral } from '../../type/literal/index'
|
||||
import type { TImport } from '../../type/module/index'
|
||||
import { Never, type TNever } from '../../type/never/index'
|
||||
import type { TNot } from '../../type/not/index'
|
||||
import type { TNull } from '../../type/null/index'
|
||||
@@ -185,6 +186,11 @@ function FromDate(schema: TDate, references: TSchema[], value: any): boolean {
|
||||
function FromFunction(schema: TFunction, references: TSchema[], value: any): boolean {
|
||||
return IsFunction(value)
|
||||
}
|
||||
function FromImport(schema: TImport, references: TSchema[], value: any): boolean {
|
||||
const definitions = globalThis.Object.values(schema.$defs) as TSchema[]
|
||||
const target = schema.$defs[schema.$ref] as TSchema
|
||||
return Visit(target, [...references, ...definitions], value)
|
||||
}
|
||||
function FromInteger(schema: TInteger, references: TSchema[], value: any): boolean {
|
||||
if (!IsInteger(value)) {
|
||||
return false
|
||||
@@ -415,7 +421,7 @@ function FromKind(schema: TSchema, references: TSchema[], value: unknown): boole
|
||||
return func(schema, value)
|
||||
}
|
||||
function Visit<T extends TSchema>(schema: T, references: TSchema[], value: any): boolean {
|
||||
const references_ = IsDefined<string>(schema.$id) ? [...references, schema] : references
|
||||
const references_ = IsDefined<string>(schema.$id) ? Pushref(schema, references) : references
|
||||
const schema_ = schema as any
|
||||
switch (schema_[Kind]) {
|
||||
case 'Any':
|
||||
@@ -434,6 +440,8 @@ function Visit<T extends TSchema>(schema: T, references: TSchema[], value: any):
|
||||
return FromDate(schema_, references_, value)
|
||||
case 'Function':
|
||||
return FromFunction(schema_, references_, value)
|
||||
case 'Import':
|
||||
return FromImport(schema_, references_, value)
|
||||
case 'Integer':
|
||||
return FromInteger(schema_, references_, value)
|
||||
case 'Intersect':
|
||||
|
||||
@@ -29,11 +29,12 @@ THE SOFTWARE.
|
||||
import { KeyOfPropertyKeys } from '../../type/keyof/index'
|
||||
import { Check } from '../check/index'
|
||||
import { Clone } from '../clone/index'
|
||||
import { Deref } from '../deref/index'
|
||||
import { Deref, Pushref } from '../deref/index'
|
||||
import { Kind } from '../../type/symbols/index'
|
||||
|
||||
import type { TSchema } from '../../type/schema/index'
|
||||
import type { TArray } from '../../type/array/index'
|
||||
import type { TImport } from 'src/type/module/index'
|
||||
import type { TIntersect } from '../../type/intersect/index'
|
||||
import type { TObject } from '../../type/object/index'
|
||||
import type { TRecord } from '../../type/record/index'
|
||||
@@ -60,6 +61,7 @@ import {
|
||||
import {
|
||||
IsKind
|
||||
} from '../../type/guard/kind'
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// IsCheckable
|
||||
// ------------------------------------------------------------------
|
||||
@@ -73,6 +75,11 @@ function FromArray(schema: TArray, references: TSchema[], value: unknown): any {
|
||||
if (!IsArray(value)) return value
|
||||
return value.map((value) => Visit(schema.items, references, value))
|
||||
}
|
||||
function FromImport(schema: TImport, references: TSchema[], value: unknown): any {
|
||||
const definitions = globalThis.Object.values(schema.$defs) as TSchema[]
|
||||
const target = schema.$defs[schema.$ref] as TSchema
|
||||
return Visit(target, [...references, ...definitions], value)
|
||||
}
|
||||
function FromIntersect(schema: TIntersect, references: TSchema[], value: unknown): any {
|
||||
const unevaluatedProperties = schema.unevaluatedProperties as TSchema
|
||||
const intersections = schema.allOf.map((schema) => Visit(schema, references, Clone(value)))
|
||||
@@ -149,11 +156,13 @@ function FromUnion(schema: TUnion, references: TSchema[], value: unknown): any {
|
||||
return value
|
||||
}
|
||||
function Visit(schema: TSchema, references: TSchema[], value: unknown): unknown {
|
||||
const references_ = IsString(schema.$id) ? [...references, schema] : references
|
||||
const references_ = IsString(schema.$id) ? Pushref(schema, references) : references
|
||||
const schema_ = schema as any
|
||||
switch (schema_[Kind]) {
|
||||
case 'Array':
|
||||
return FromArray(schema_, references_, value)
|
||||
case 'Import':
|
||||
return FromImport(schema_, references_, value)
|
||||
case 'Intersect':
|
||||
return FromIntersect(schema_, references_, value)
|
||||
case 'Object':
|
||||
|
||||
@@ -38,6 +38,7 @@ import type { TBoolean } from '../../type/boolean/index'
|
||||
import type { TDate } from '../../type/date/index'
|
||||
import type { TInteger } from '../../type/integer/index'
|
||||
import type { TIntersect } from '../../type/intersect/index'
|
||||
import type { TImport } from '../../type/module/index'
|
||||
import type { TLiteral } from '../../type/literal/index'
|
||||
import type { TNull } from '../../type/null/index'
|
||||
import type { TNumber } from '../../type/number/index'
|
||||
@@ -182,6 +183,11 @@ function FromBoolean(schema: TBoolean, references: TSchema[], value: any): unkno
|
||||
function FromDate(schema: TDate, references: TSchema[], value: any): unknown {
|
||||
return TryConvertDate(value)
|
||||
}
|
||||
function FromImport(schema: TImport, references: TSchema[], value: unknown): unknown {
|
||||
const definitions = globalThis.Object.values(schema.$defs) as TSchema[]
|
||||
const target = schema.$defs[schema.$ref] as TSchema
|
||||
return Visit(target, [...references, ...definitions], value)
|
||||
}
|
||||
function FromInteger(schema: TInteger, references: TSchema[], value: any): unknown {
|
||||
return TryConvertInteger(value)
|
||||
}
|
||||
@@ -261,6 +267,8 @@ function Visit(schema: TSchema, references: TSchema[], value: any): unknown {
|
||||
return FromBoolean(schema_, references_, value)
|
||||
case 'Date':
|
||||
return FromDate(schema_, references_, value)
|
||||
case 'Import':
|
||||
return FromImport(schema_, references_, value)
|
||||
case 'Integer':
|
||||
return FromInteger(schema_, references_, value)
|
||||
case 'Intersect':
|
||||
|
||||
@@ -45,6 +45,7 @@ import type { TBoolean } from '../../type/boolean/index'
|
||||
import type { TDate } from '../../type/date/index'
|
||||
import type { TConstructor } from '../../type/constructor/index'
|
||||
import type { TFunction } from '../../type/function/index'
|
||||
import type { TImport } from '../../type/module/index'
|
||||
import type { TInteger } from '../../type/integer/index'
|
||||
import type { TIntersect } from '../../type/intersect/index'
|
||||
import type { TIterator } from '../../type/iterator/index'
|
||||
@@ -167,6 +168,11 @@ function FromFunction(schema: TFunction, references: TSchema[]): any {
|
||||
return () => Visit(schema.returns, references)
|
||||
}
|
||||
}
|
||||
function FromImport(schema: TImport, references: TSchema[]): any {
|
||||
const definitions = globalThis.Object.values(schema.$defs) as TSchema[]
|
||||
const target = schema.$defs[schema.$ref] as TSchema
|
||||
return Visit(target, [...references, ...definitions])
|
||||
}
|
||||
function FromInteger(schema: TInteger, references: TSchema[]): any {
|
||||
if (HasPropertyKey(schema, 'default')) {
|
||||
return FromDefault(schema.default)
|
||||
@@ -411,6 +417,8 @@ function Visit(schema: TSchema, references: TSchema[]): unknown {
|
||||
return FromDate(schema_, references_)
|
||||
case 'Function':
|
||||
return FromFunction(schema_, references_)
|
||||
case 'Import':
|
||||
return FromImport(schema_, references_)
|
||||
case 'Integer':
|
||||
return FromInteger(schema_, references_)
|
||||
case 'Intersect':
|
||||
|
||||
@@ -33,6 +33,7 @@ import { Kind } from '../../type/symbols/index'
|
||||
|
||||
import type { TSchema } from '../../type/schema/index'
|
||||
import type { TArray } from '../../type/array/index'
|
||||
import type { TImport } from '../../type/module/index'
|
||||
import type { TIntersect } from '../../type/intersect/index'
|
||||
import type { TObject } from '../../type/object/index'
|
||||
import type { TRecord } from '../../type/record/index'
|
||||
@@ -78,6 +79,11 @@ function FromDate(schema: TArray, references: TSchema[], value: unknown): any {
|
||||
// special case intercept for dates
|
||||
return IsDate(value) ? value : ValueOrDefault(schema, value)
|
||||
}
|
||||
function FromImport(schema: TImport, references: TSchema[], value: unknown): any {
|
||||
const definitions = globalThis.Object.values(schema.$defs) as TSchema[]
|
||||
const target = schema.$defs[schema.$ref] as TSchema
|
||||
return Visit(target, [...references, ...definitions], value)
|
||||
}
|
||||
function FromIntersect(schema: TIntersect, references: TSchema[], value: unknown): any {
|
||||
const defaulted = ValueOrDefault(schema, value)
|
||||
return schema.allOf.reduce((acc, schema) => {
|
||||
@@ -161,6 +167,8 @@ function Visit(schema: TSchema, references: TSchema[], value: unknown): any {
|
||||
return FromArray(schema_, references_, value)
|
||||
case 'Date':
|
||||
return FromDate(schema_, references_, value)
|
||||
case 'Import':
|
||||
return FromImport(schema_, references_, value)
|
||||
case 'Intersect':
|
||||
return FromIntersect(schema_, references_, value)
|
||||
case 'Object':
|
||||
|
||||
@@ -36,6 +36,7 @@ import { Check } from '../check/index'
|
||||
|
||||
import type { TSchema } from '../../type/schema/index'
|
||||
import type { TArray } from '../../type/array/index'
|
||||
import type { TImport } from '../../type/module/index'
|
||||
import type { TIntersect } from '../../type/intersect/index'
|
||||
import type { TNot } from '../../type/not/index'
|
||||
import type { TObject } from '../../type/object/index'
|
||||
@@ -82,7 +83,7 @@ export class TransformDecodeError extends TypeBoxError {
|
||||
// Decode
|
||||
// ------------------------------------------------------------------
|
||||
// prettier-ignore
|
||||
function Default(schema: TSchema, path: string, value: any) {
|
||||
function Default(schema: TSchema, path: string, value: any): unknown {
|
||||
try {
|
||||
return IsTransform(schema) ? schema[TransformKind].Decode(value) : value
|
||||
} catch (error) {
|
||||
@@ -90,13 +91,13 @@ function Default(schema: TSchema, path: string, value: any) {
|
||||
}
|
||||
}
|
||||
// prettier-ignore
|
||||
function FromArray(schema: TArray, references: TSchema[], path: string, value: any): any {
|
||||
function FromArray(schema: TArray, references: TSchema[], path: string, value: any): unknown {
|
||||
return (IsArray(value))
|
||||
? Default(schema, path, value.map((value: any, index) => Visit(schema.items, references, `${path}/${index}`, value)))
|
||||
: Default(schema, path, value)
|
||||
}
|
||||
// prettier-ignore
|
||||
function FromIntersect(schema: TIntersect, references: TSchema[], path: string, value: any) {
|
||||
function FromIntersect(schema: TIntersect, references: TSchema[], path: string, value: any): unknown {
|
||||
if (!IsObject(value) || IsValueType(value)) return Default(schema, path, value)
|
||||
const knownEntries = KeyOfPropertyEntries(schema)
|
||||
const knownKeys = knownEntries.map(entry => entry[0])
|
||||
@@ -115,11 +116,20 @@ function FromIntersect(schema: TIntersect, references: TSchema[], path: string,
|
||||
}
|
||||
return Default(schema, path, unknownProperties)
|
||||
}
|
||||
function FromNot(schema: TNot, references: TSchema[], path: string, value: any) {
|
||||
// prettier-ignore
|
||||
function FromImport(schema: TImport, references: TSchema[], path: string, value: unknown): unknown {
|
||||
const definitions = globalThis.Object.values(schema.$defs) as TSchema[]
|
||||
const target = schema.$defs[schema.$ref] as TSchema
|
||||
const transform = schema[TransformKind as never]
|
||||
// Note: we need to re-spec the target as TSchema + [TransformKind]
|
||||
const transformTarget = { [TransformKind]: transform, ...target } as TSchema
|
||||
return Visit(transformTarget as never, [...references, ...definitions], path, value)
|
||||
}
|
||||
function FromNot(schema: TNot, references: TSchema[], path: string, value: any): unknown {
|
||||
return Default(schema, path, Visit(schema.not, references, path, value))
|
||||
}
|
||||
// prettier-ignore
|
||||
function FromObject(schema: TObject, references: TSchema[], path: string, value: any) {
|
||||
function FromObject(schema: TObject, references: TSchema[], path: string, value: any): unknown {
|
||||
if (!IsObject(value)) return Default(schema, path, value)
|
||||
const knownKeys = KeyOfPropertyKeys(schema) as string[]
|
||||
const knownProperties = { ...value } as Record<PropertyKey, unknown>
|
||||
@@ -147,7 +157,7 @@ function FromObject(schema: TObject, references: TSchema[], path: string, value:
|
||||
return Default(schema, path, unknownProperties)
|
||||
}
|
||||
// prettier-ignore
|
||||
function FromRecord(schema: TRecord, references: TSchema[], path: string, value: any) {
|
||||
function FromRecord(schema: TRecord, references: TSchema[], path: string, value: any): unknown {
|
||||
if (!IsObject(value)) return Default(schema, path, value)
|
||||
const pattern = Object.getOwnPropertyNames(schema.patternProperties)[0]
|
||||
const knownKeys = new RegExp(pattern)
|
||||
@@ -167,23 +177,23 @@ function FromRecord(schema: TRecord, references: TSchema[], path: string, value:
|
||||
return Default(schema, path, unknownProperties)
|
||||
}
|
||||
// prettier-ignore
|
||||
function FromRef(schema: TRef, references: TSchema[], path: string, value: any) {
|
||||
function FromRef(schema: TRef, references: TSchema[], path: string, value: any): unknown {
|
||||
const target = Deref(schema, references)
|
||||
return Default(schema, path, Visit(target, references, path, value))
|
||||
}
|
||||
// prettier-ignore
|
||||
function FromThis(schema: TThis, references: TSchema[], path: string, value: any) {
|
||||
function FromThis(schema: TThis, references: TSchema[], path: string, value: any): unknown {
|
||||
const target = Deref(schema, references)
|
||||
return Default(schema, path, Visit(target, references, path, value))
|
||||
}
|
||||
// prettier-ignore
|
||||
function FromTuple(schema: TTuple, references: TSchema[], path: string, value: any) {
|
||||
function FromTuple(schema: TTuple, references: TSchema[], path: string, value: any): unknown {
|
||||
return (IsArray(value) && IsArray(schema.items))
|
||||
? Default(schema, path, schema.items.map((schema, index) => Visit(schema, references, `${path}/${index}`, value[index])))
|
||||
: Default(schema, path, value)
|
||||
}
|
||||
// prettier-ignore
|
||||
function FromUnion(schema: TUnion, references: TSchema[], path: string, value: any) {
|
||||
function FromUnion(schema: TUnion, references: TSchema[], path: string, value: any): unknown {
|
||||
for (const subschema of schema.anyOf) {
|
||||
if (!Check(subschema, references, value)) continue
|
||||
// note: ensure interior is decoded first
|
||||
@@ -199,6 +209,8 @@ function Visit(schema: TSchema, references: TSchema[], path: string, value: any)
|
||||
switch (schema[Kind]) {
|
||||
case 'Array':
|
||||
return FromArray(schema_, references_, path, value)
|
||||
case 'Import':
|
||||
return FromImport(schema_, references_, path, value)
|
||||
case 'Intersect':
|
||||
return FromIntersect(schema_, references_, path, value)
|
||||
case 'Not':
|
||||
|
||||
@@ -36,6 +36,7 @@ import { Check } from '../check/index'
|
||||
|
||||
import type { TSchema } from '../../type/schema/index'
|
||||
import type { TArray } from '../../type/array/index'
|
||||
import type { TImport } from '../../type/module/index'
|
||||
import type { TIntersect } from '../../type/intersect/index'
|
||||
import type { TNot } from '../../type/not/index'
|
||||
import type { TObject } from '../../type/object/index'
|
||||
@@ -96,6 +97,15 @@ function FromArray(schema: TArray, references: TSchema[], path: string, value: a
|
||||
: defaulted
|
||||
}
|
||||
// prettier-ignore
|
||||
function FromImport(schema: TImport, references: TSchema[], path: string, value: unknown): unknown {
|
||||
const definitions = globalThis.Object.values(schema.$defs) as TSchema[]
|
||||
const target = schema.$defs[schema.$ref] as TSchema
|
||||
const transform = schema[TransformKind as never]
|
||||
// Note: we need to re-spec the target as TSchema + [TransformKind]
|
||||
const transformTarget = { [TransformKind]: transform, ...target } as TSchema
|
||||
return Visit(transformTarget as never, [...references, ...definitions], path, value)
|
||||
}
|
||||
// prettier-ignore
|
||||
function FromIntersect(schema: TIntersect, references: TSchema[], path: string, value: any) {
|
||||
const defaulted = Default(schema, path, value)
|
||||
if (!IsObject(value) || IsValueType(value)) return defaulted
|
||||
@@ -210,6 +220,8 @@ function Visit(schema: TSchema, references: TSchema[], path: string, value: any)
|
||||
switch (schema[Kind]) {
|
||||
case 'Array':
|
||||
return FromArray(schema_, references_, path, value)
|
||||
case 'Import':
|
||||
return FromImport(schema_, references_, path, value)
|
||||
case 'Intersect':
|
||||
return FromIntersect(schema_, references_, path, value)
|
||||
case 'Not':
|
||||
|
||||
@@ -9,6 +9,7 @@ import './integer'
|
||||
import './intersect'
|
||||
import './keyof'
|
||||
import './literal'
|
||||
import './module'
|
||||
import './never'
|
||||
import './not'
|
||||
import './null'
|
||||
|
||||
78
test/runtime/compiler-ajv/module.ts
Normal file
78
test/runtime/compiler-ajv/module.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
|
||||
describe('compiler-ajv/Module', () => {
|
||||
it('Should validate string', () => {
|
||||
const Module = Type.Module({
|
||||
A: Type.String(),
|
||||
})
|
||||
const T = Module.Import('A')
|
||||
Ok(T, 'hello')
|
||||
Fail(T, true)
|
||||
})
|
||||
it('Should validate referenced string', () => {
|
||||
const Module = Type.Module({
|
||||
A: Type.String(),
|
||||
B: Type.Ref('A'),
|
||||
})
|
||||
const T = Module.Import('B')
|
||||
Ok(T, 'hello')
|
||||
Fail(T, true)
|
||||
})
|
||||
it('Should validate self referential', () => {
|
||||
const Module = Type.Module({
|
||||
A: Type.Object({
|
||||
nodes: Type.Array(Type.Ref('A')),
|
||||
}),
|
||||
})
|
||||
const T = Module.Import('A')
|
||||
Ok(T, { nodes: [{ nodes: [{ nodes: [] }, { nodes: [] }] }] })
|
||||
Fail(T, { nodes: [{ nodes: [{ nodes: [] }, { nodes: false }] }] })
|
||||
Fail(T, true)
|
||||
})
|
||||
it('Should validate mutual recursive', () => {
|
||||
const Module = Type.Module({
|
||||
A: Type.Object({
|
||||
b: Type.Ref('B'),
|
||||
}),
|
||||
B: Type.Object({
|
||||
a: Type.Union([Type.Ref('A'), Type.Null()]),
|
||||
}),
|
||||
})
|
||||
const T = Module.Import('A')
|
||||
Ok(T, { b: { a: null } })
|
||||
Ok(T, { b: { a: { b: { a: null } } } })
|
||||
Fail(T, { b: { a: 1 } })
|
||||
Fail(T, { b: { a: { b: { a: 1 } } } })
|
||||
Fail(T, true)
|
||||
})
|
||||
it('Should validate mutual recursive (Array)', () => {
|
||||
const Module = Type.Module({
|
||||
A: Type.Object({
|
||||
b: Type.Ref('B'),
|
||||
}),
|
||||
B: Type.Object({
|
||||
a: Type.Array(Type.Ref('A')),
|
||||
}),
|
||||
})
|
||||
const T = Module.Import('A')
|
||||
Ok(T, { b: { a: [{ b: { a: [] } }] } })
|
||||
Fail(T, { b: { a: [{ b: { a: [null] } }] } })
|
||||
Fail(T, true)
|
||||
})
|
||||
it('Should validate deep referential', () => {
|
||||
const Module = Type.Module({
|
||||
A: Type.Ref('B'),
|
||||
B: Type.Ref('C'),
|
||||
C: Type.Ref('D'),
|
||||
D: Type.Ref('E'),
|
||||
E: Type.Ref('F'),
|
||||
F: Type.Ref('G'),
|
||||
G: Type.Ref('H'),
|
||||
H: Type.Literal('hello'),
|
||||
})
|
||||
const T = Module.Import('A')
|
||||
Ok(T, 'hello')
|
||||
Fail(T, 'world')
|
||||
})
|
||||
})
|
||||
@@ -11,7 +11,7 @@ describe('compiler-ajv/Ref', () => {
|
||||
},
|
||||
{ $id: 'T' },
|
||||
)
|
||||
const R = Type.Ref(T)
|
||||
const R = Type.Ref(T.$id!)
|
||||
Ok(
|
||||
R,
|
||||
{
|
||||
@@ -31,7 +31,7 @@ describe('compiler-ajv/Ref', () => {
|
||||
},
|
||||
{ $id: 'T' },
|
||||
)
|
||||
const R = Type.Ref(T)
|
||||
const R = Type.Ref(T.$id!)
|
||||
Fail(
|
||||
R,
|
||||
{
|
||||
@@ -54,7 +54,7 @@ describe('compiler-ajv/Ref', () => {
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
z: Type.Number(),
|
||||
r: Type.Optional(Type.Ref(R)),
|
||||
r: Type.Optional(Type.Ref(R.$id!)),
|
||||
},
|
||||
{ $id: 'T' },
|
||||
)
|
||||
|
||||
@@ -16,6 +16,7 @@ import './iterator'
|
||||
import './keyof'
|
||||
import './kind'
|
||||
import './literal'
|
||||
import './module'
|
||||
import './never'
|
||||
import './not'
|
||||
import './null'
|
||||
|
||||
78
test/runtime/compiler/module.ts
Normal file
78
test/runtime/compiler/module.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Ok, Fail } from './validate'
|
||||
|
||||
describe('compiler/Module', () => {
|
||||
it('Should validate string', () => {
|
||||
const Module = Type.Module({
|
||||
A: Type.String(),
|
||||
})
|
||||
const T = Module.Import('A')
|
||||
Ok(T, 'hello')
|
||||
Fail(T, true)
|
||||
})
|
||||
it('Should validate referenced string', () => {
|
||||
const Module = Type.Module({
|
||||
A: Type.String(),
|
||||
B: Type.Ref('A'),
|
||||
})
|
||||
const T = Module.Import('B')
|
||||
Ok(T, 'hello')
|
||||
Fail(T, true)
|
||||
})
|
||||
it('Should validate self referential', () => {
|
||||
const Module = Type.Module({
|
||||
A: Type.Object({
|
||||
nodes: Type.Array(Type.Ref('A')),
|
||||
}),
|
||||
})
|
||||
const T = Module.Import('A')
|
||||
Ok(T, { nodes: [{ nodes: [{ nodes: [] }, { nodes: [] }] }] })
|
||||
Fail(T, { nodes: [{ nodes: [{ nodes: [] }, { nodes: false }] }] })
|
||||
Fail(T, true)
|
||||
})
|
||||
it('Should validate mutual recursive', () => {
|
||||
const Module = Type.Module({
|
||||
A: Type.Object({
|
||||
b: Type.Ref('B'),
|
||||
}),
|
||||
B: Type.Object({
|
||||
a: Type.Union([Type.Ref('A'), Type.Null()]),
|
||||
}),
|
||||
})
|
||||
const T = Module.Import('A')
|
||||
Ok(T, { b: { a: null } })
|
||||
Ok(T, { b: { a: { b: { a: null } } } })
|
||||
Fail(T, { b: { a: 1 } })
|
||||
Fail(T, { b: { a: { b: { a: 1 } } } })
|
||||
Fail(T, true)
|
||||
})
|
||||
it('Should validate mutual recursive (Array)', () => {
|
||||
const Module = Type.Module({
|
||||
A: Type.Object({
|
||||
b: Type.Ref('B'),
|
||||
}),
|
||||
B: Type.Object({
|
||||
a: Type.Array(Type.Ref('A')),
|
||||
}),
|
||||
})
|
||||
const T = Module.Import('A')
|
||||
Ok(T, { b: { a: [{ b: { a: [] } }] } })
|
||||
Fail(T, { b: { a: [{ b: { a: [null] } }] } })
|
||||
Fail(T, true)
|
||||
})
|
||||
it('Should validate deep referential', () => {
|
||||
const Module = Type.Module({
|
||||
A: Type.Ref('B'),
|
||||
B: Type.Ref('C'),
|
||||
C: Type.Ref('D'),
|
||||
D: Type.Ref('E'),
|
||||
E: Type.Ref('F'),
|
||||
F: Type.Ref('G'),
|
||||
G: Type.Ref('H'),
|
||||
H: Type.Literal('hello'),
|
||||
})
|
||||
const T = Module.Import('A')
|
||||
Ok(T, 'hello')
|
||||
Fail(T, 'world')
|
||||
})
|
||||
})
|
||||
@@ -12,7 +12,7 @@ describe('compiler/Ref', () => {
|
||||
},
|
||||
{ $id: Assert.NextId() },
|
||||
)
|
||||
const R = Type.Ref(T)
|
||||
const R = Type.Ref(T.$id!)
|
||||
Ok(
|
||||
R,
|
||||
{
|
||||
@@ -32,7 +32,7 @@ describe('compiler/Ref', () => {
|
||||
},
|
||||
{ $id: Assert.NextId() },
|
||||
)
|
||||
const R = Type.Ref(T)
|
||||
const R = Type.Ref(T.$id!)
|
||||
Fail(
|
||||
R,
|
||||
{
|
||||
@@ -54,7 +54,7 @@ describe('compiler/Ref', () => {
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
z: Type.Number(),
|
||||
r: Type.Optional(Type.Ref(T)),
|
||||
r: Type.Optional(Type.Ref(T.$id!)),
|
||||
},
|
||||
{ $id: 'T' },
|
||||
)
|
||||
@@ -70,7 +70,7 @@ describe('compiler/Ref', () => {
|
||||
nodes: Type.Array(Node),
|
||||
}),
|
||||
)
|
||||
const R = Type.Ref(T)
|
||||
const R = Type.Ref(T.$id!)
|
||||
Ok(R, { id: '', nodes: [{ id: '', nodes: [] }] }, [T])
|
||||
Fail(R, { id: '', nodes: [{ id: 1, nodes: [] }] }, [T])
|
||||
})
|
||||
|
||||
@@ -31,7 +31,7 @@ describe('compiler/Unicode', () => {
|
||||
},
|
||||
)
|
||||
const T = Type.Object({
|
||||
vector: Type.Ref(R),
|
||||
vector: Type.Ref(R.$id!),
|
||||
})
|
||||
Ok(
|
||||
T,
|
||||
|
||||
@@ -1,10 +1,100 @@
|
||||
import { TypeGuard } from '@sinclair/typebox'
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Type, TModule } from '@sinclair/typebox'
|
||||
import { Parse } from '@sinclair/typebox/syntax'
|
||||
import { Assert } from '../assert/index'
|
||||
|
||||
// prettier-ignore
|
||||
describe('parse/Parse', () => {
|
||||
describe('syntax/Parse', () => {
|
||||
// ----------------------------------------------------------------
|
||||
// Type Alias
|
||||
// ----------------------------------------------------------------
|
||||
it('Should parse Type Alias 1', () => {
|
||||
const T = Parse('type A = 1')
|
||||
Assert.IsTrue(TypeGuard.IsLiteral(T.A))
|
||||
Assert.IsTrue(T.A.const === 1)
|
||||
})
|
||||
it('Should parse Type Alias 2', () => {
|
||||
const T = Parse('export type A = 1')
|
||||
Assert.IsTrue(TypeGuard.IsLiteral(T.A))
|
||||
Assert.IsTrue(T.A.const === 1)
|
||||
})
|
||||
// ----------------------------------------------------------------
|
||||
// Interface
|
||||
// ----------------------------------------------------------------
|
||||
it('Should parse Interface 1', () => {
|
||||
const T = Parse('interface A { x: 1 }')
|
||||
Assert.IsTrue(TypeGuard.IsObject(T.A))
|
||||
Assert.IsTrue(TypeGuard.IsLiteral(T.A.properties.x))
|
||||
Assert.IsTrue(T.A.properties.x.const === 1)
|
||||
})
|
||||
it('Should parse Interface 2', () => {
|
||||
const T = Parse('export interface A { x: 1 }')
|
||||
Assert.IsTrue(TypeGuard.IsObject(T.A))
|
||||
Assert.IsTrue(TypeGuard.IsLiteral(T.A.properties.x))
|
||||
Assert.IsTrue(T.A.properties.x.const === 1)
|
||||
})
|
||||
// ----------------------------------------------------------------
|
||||
// Module
|
||||
// ----------------------------------------------------------------
|
||||
it('Should parse Module 1', () => {
|
||||
const T = Parse('module {}')
|
||||
Assert.IsTrue(T instanceof TModule)
|
||||
})
|
||||
it('Should parse Module 2', () => {
|
||||
const T = Parse('export module {}')
|
||||
Assert.IsTrue(T instanceof TModule)
|
||||
})
|
||||
it('Should parse Module 3', () => {
|
||||
const T = Parse('module A {}')
|
||||
Assert.IsTrue(T instanceof TModule)
|
||||
})
|
||||
it('Should parse Module 4', () => {
|
||||
const T = Parse('export module A {}')
|
||||
Assert.IsTrue(T instanceof TModule)
|
||||
})
|
||||
it('Should parse Module 5', () => {
|
||||
const T = Parse(`export module A {
|
||||
export type A = number
|
||||
}`)
|
||||
const A = T.Import('A')
|
||||
Assert.IsTrue(T instanceof TModule)
|
||||
Assert.IsTrue(TypeGuard.IsImport(A))
|
||||
const N = A.$defs[A.$ref]
|
||||
Assert.IsTrue(TypeGuard.IsNumber(N))
|
||||
})
|
||||
it('Should parse Module 6', () => {
|
||||
const T = Parse(`export module A {
|
||||
export interface A { x: number }
|
||||
}`)
|
||||
const A = T.Import('A')
|
||||
Assert.IsTrue(T instanceof TModule)
|
||||
Assert.IsTrue(TypeGuard.IsImport(A))
|
||||
const N = A.$defs[A.$ref]
|
||||
Assert.IsTrue(TypeGuard.IsObject(N))
|
||||
Assert.IsTrue(TypeGuard.IsNumber(N.properties.x))
|
||||
})
|
||||
it('Should parse Module 7', () => {
|
||||
const T = Parse(`export module A {
|
||||
export interface A { x: number }
|
||||
export type B = number
|
||||
}`)
|
||||
// A
|
||||
const A = T.Import('A')
|
||||
Assert.IsTrue(T instanceof TModule)
|
||||
Assert.IsTrue(TypeGuard.IsImport(A))
|
||||
const N1 = A.$defs[A.$ref]
|
||||
Assert.IsTrue(TypeGuard.IsObject(N1))
|
||||
Assert.IsTrue(TypeGuard.IsNumber(N1.properties.x))
|
||||
// B
|
||||
const B = T.Import('B')
|
||||
Assert.IsTrue(T instanceof TModule)
|
||||
Assert.IsTrue(TypeGuard.IsImport(B))
|
||||
const N2 = B.$defs[B.$ref]
|
||||
Assert.IsTrue(TypeGuard.IsNumber(N2))
|
||||
})
|
||||
// ----------------------------------------------------------------
|
||||
// Type Expressions
|
||||
// ----------------------------------------------------------------
|
||||
it('Should parse Any', () => {
|
||||
const T = Parse(`any`)
|
||||
Assert.IsTrue(TypeGuard.IsAny(T))
|
||||
|
||||
@@ -1,110 +0,0 @@
|
||||
import { KindGuard } from '@sinclair/typebox'
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Assert } from '../../../assert/index'
|
||||
|
||||
describe('guard/kind/TDeref', () => {
|
||||
it('Should should deref 1', () => {
|
||||
const T = Type.String({ $id: 'T' })
|
||||
const R = Type.Ref(T)
|
||||
const D = Type.Deref(R, [T])
|
||||
Assert.IsTrue(KindGuard.IsString(D))
|
||||
Assert.IsFalse('$id' in D)
|
||||
})
|
||||
it('Should should deref 2', () => {
|
||||
const T = Type.String({ $id: 'T' })
|
||||
const R = Type.Ref(T)
|
||||
const O = Type.Object({
|
||||
x: R,
|
||||
y: R,
|
||||
})
|
||||
const D = Type.Deref(O, [T])
|
||||
Assert.IsTrue(KindGuard.IsObject(D))
|
||||
Assert.IsTrue(KindGuard.IsString(D.properties.x))
|
||||
Assert.IsTrue(KindGuard.IsString(D.properties.y))
|
||||
Assert.IsFalse('$id' in D.properties.x)
|
||||
Assert.IsFalse('$id' in D.properties.y)
|
||||
})
|
||||
it('Should should deref 3', () => {
|
||||
const T = Type.String({ $id: 'T' })
|
||||
const R = Type.Ref(T)
|
||||
const O = Type.Array(R)
|
||||
const D = Type.Deref(O, [T])
|
||||
Assert.IsTrue(KindGuard.IsArray(D))
|
||||
Assert.IsTrue(KindGuard.IsString(D.items))
|
||||
Assert.IsFalse('$id' in D.items)
|
||||
})
|
||||
it('Should should deref 4', () => {
|
||||
const T = Type.String({ $id: 'T' })
|
||||
const R = Type.Ref(T)
|
||||
const O = Type.AsyncIterator(R)
|
||||
const D = Type.Deref(O, [T])
|
||||
Assert.IsTrue(KindGuard.IsAsyncIterator(D))
|
||||
Assert.IsTrue(KindGuard.IsString(D.items))
|
||||
Assert.IsFalse('$id' in D.items)
|
||||
})
|
||||
it('Should should deref 5', () => {
|
||||
const T = Type.String({ $id: 'T' })
|
||||
const R = Type.Ref(T)
|
||||
const O = Type.Iterator(R)
|
||||
const D = Type.Deref(O, [T])
|
||||
Assert.IsTrue(KindGuard.IsIterator(D))
|
||||
Assert.IsTrue(KindGuard.IsString(D.items))
|
||||
Assert.IsFalse('$id' in D.items)
|
||||
})
|
||||
it('Should should deref 6', () => {
|
||||
const T = Type.String({ $id: 'T' })
|
||||
const R = Type.Ref(T)
|
||||
const O = Type.Function([R], R)
|
||||
const D = Type.Deref(O, [T])
|
||||
Assert.IsTrue(KindGuard.IsFunction(D))
|
||||
Assert.IsTrue(KindGuard.IsString(D.parameters[0]))
|
||||
Assert.IsTrue(KindGuard.IsString(D.returns))
|
||||
Assert.IsFalse('$id' in D.parameters[0])
|
||||
Assert.IsFalse('$id' in D.returns)
|
||||
})
|
||||
it('Should should deref 7', () => {
|
||||
const T = Type.String({ $id: 'T' })
|
||||
const R = Type.Ref(T)
|
||||
const O = Type.Constructor([R], R)
|
||||
const D = Type.Deref(O, [T])
|
||||
Assert.IsTrue(KindGuard.IsConstructor(D))
|
||||
Assert.IsTrue(KindGuard.IsString(D.parameters[0]))
|
||||
Assert.IsTrue(KindGuard.IsString(D.returns))
|
||||
Assert.IsFalse('$id' in D.parameters[0])
|
||||
Assert.IsFalse('$id' in D.returns)
|
||||
})
|
||||
it('Should should deref 8', () => {
|
||||
const T = Type.String({ $id: 'T' })
|
||||
const R = Type.Ref(T)
|
||||
const O = Type.Promise(R)
|
||||
const D = Type.Deref(O, [T])
|
||||
Assert.IsTrue(KindGuard.IsPromise(D))
|
||||
Assert.IsTrue(KindGuard.IsString(D.item))
|
||||
Assert.IsFalse('$id' in D.item)
|
||||
})
|
||||
it('Should should deref 9', () => {
|
||||
const T = Type.String({ $id: 'T' })
|
||||
const R1 = Type.Ref(T, { $id: 'R1' })
|
||||
const R2 = Type.Ref(R1, { $id: 'R2' })
|
||||
const R3 = Type.Ref(R2, { $id: 'R3' })
|
||||
const R4 = Type.Ref(R3, { $id: 'R4' })
|
||||
const R5 = Type.Ref(R4, { $id: 'R5' })
|
||||
const R6 = Type.Ref(R5, { $id: 'R6' })
|
||||
const O = Type.Array(R6)
|
||||
const D = Type.Deref(O, [R6, R5, R4, R3, R2, R1, T])
|
||||
Assert.IsTrue(KindGuard.IsArray(D))
|
||||
Assert.IsTrue(KindGuard.IsString(D.items))
|
||||
Assert.IsFalse('$id' in D.items)
|
||||
})
|
||||
it('Should should deref 10', () => {
|
||||
const T = Type.String({ $id: 'T' })
|
||||
const R1 = Type.Ref(T, { $id: 'R1' })
|
||||
const R2 = Type.Ref(R1, { $id: 'R2' })
|
||||
const R3 = Type.Ref(R2, { $id: 'R3' })
|
||||
const R4 = Type.Ref(R3, { $id: 'R4' })
|
||||
const R5 = Type.Ref(R4, { $id: 'R5' })
|
||||
const R6 = Type.Ref(R5, { $id: 'R6' })
|
||||
const O = Type.Array(R6)
|
||||
Assert.Throws(() => Type.Deref(O, [R6, R5, R4, R3, R2, R1])) // Omit T
|
||||
})
|
||||
})
|
||||
15
test/runtime/type/guard/kind/import.ts
Normal file
15
test/runtime/type/guard/kind/import.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { KindGuard } from '@sinclair/typebox'
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Assert } from '../../../assert/index'
|
||||
|
||||
describe('guard/type/TImport', () => {
|
||||
it('Should guard for TImport', () => {
|
||||
const M = Type.Module({
|
||||
A: Type.String(),
|
||||
})
|
||||
const I = M.Import('A')
|
||||
const N = I.$defs[I.$ref]
|
||||
Assert.IsTrue(KindGuard.IsImport(I))
|
||||
Assert.IsTrue(KindGuard.IsString(N))
|
||||
})
|
||||
})
|
||||
@@ -9,11 +9,11 @@ import './composite'
|
||||
import './const'
|
||||
import './constructor'
|
||||
import './date'
|
||||
import './deref'
|
||||
import './enum'
|
||||
import './exclude'
|
||||
import './extract'
|
||||
import './function'
|
||||
import './import'
|
||||
import './indexed'
|
||||
import './integer'
|
||||
import './intersect'
|
||||
|
||||
@@ -1,110 +0,0 @@
|
||||
import { TypeGuard } from '@sinclair/typebox'
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Assert } from '../../../assert/index'
|
||||
|
||||
describe('guard/type/TDeref', () => {
|
||||
it('Should should deref 1', () => {
|
||||
const T = Type.String({ $id: 'T' })
|
||||
const R = Type.Ref(T)
|
||||
const D = Type.Deref(R, [T])
|
||||
Assert.IsTrue(TypeGuard.IsString(D))
|
||||
Assert.IsFalse('$id' in D)
|
||||
})
|
||||
it('Should should deref 2', () => {
|
||||
const T = Type.String({ $id: 'T' })
|
||||
const R = Type.Ref(T)
|
||||
const O = Type.Object({
|
||||
x: R,
|
||||
y: R,
|
||||
})
|
||||
const D = Type.Deref(O, [T])
|
||||
Assert.IsTrue(TypeGuard.IsObject(D))
|
||||
Assert.IsTrue(TypeGuard.IsString(D.properties.x))
|
||||
Assert.IsTrue(TypeGuard.IsString(D.properties.y))
|
||||
Assert.IsFalse('$id' in D.properties.x)
|
||||
Assert.IsFalse('$id' in D.properties.y)
|
||||
})
|
||||
it('Should should deref 3', () => {
|
||||
const T = Type.String({ $id: 'T' })
|
||||
const R = Type.Ref(T)
|
||||
const O = Type.Array(R)
|
||||
const D = Type.Deref(O, [T])
|
||||
Assert.IsTrue(TypeGuard.IsArray(D))
|
||||
Assert.IsTrue(TypeGuard.IsString(D.items))
|
||||
Assert.IsFalse('$id' in D.items)
|
||||
})
|
||||
it('Should should deref 4', () => {
|
||||
const T = Type.String({ $id: 'T' })
|
||||
const R = Type.Ref(T)
|
||||
const O = Type.AsyncIterator(R)
|
||||
const D = Type.Deref(O, [T])
|
||||
Assert.IsTrue(TypeGuard.IsAsyncIterator(D))
|
||||
Assert.IsTrue(TypeGuard.IsString(D.items))
|
||||
Assert.IsFalse('$id' in D.items)
|
||||
})
|
||||
it('Should should deref 5', () => {
|
||||
const T = Type.String({ $id: 'T' })
|
||||
const R = Type.Ref(T)
|
||||
const O = Type.Iterator(R)
|
||||
const D = Type.Deref(O, [T])
|
||||
Assert.IsTrue(TypeGuard.IsIterator(D))
|
||||
Assert.IsTrue(TypeGuard.IsString(D.items))
|
||||
Assert.IsFalse('$id' in D.items)
|
||||
})
|
||||
it('Should should deref 6', () => {
|
||||
const T = Type.String({ $id: 'T' })
|
||||
const R = Type.Ref(T)
|
||||
const O = Type.Function([R], R)
|
||||
const D = Type.Deref(O, [T])
|
||||
Assert.IsTrue(TypeGuard.IsFunction(D))
|
||||
Assert.IsTrue(TypeGuard.IsString(D.parameters[0]))
|
||||
Assert.IsTrue(TypeGuard.IsString(D.returns))
|
||||
Assert.IsFalse('$id' in D.parameters[0])
|
||||
Assert.IsFalse('$id' in D.returns)
|
||||
})
|
||||
it('Should should deref 7', () => {
|
||||
const T = Type.String({ $id: 'T' })
|
||||
const R = Type.Ref(T)
|
||||
const O = Type.Constructor([R], R)
|
||||
const D = Type.Deref(O, [T])
|
||||
Assert.IsTrue(TypeGuard.IsConstructor(D))
|
||||
Assert.IsTrue(TypeGuard.IsString(D.parameters[0]))
|
||||
Assert.IsTrue(TypeGuard.IsString(D.returns))
|
||||
Assert.IsFalse('$id' in D.parameters[0])
|
||||
Assert.IsFalse('$id' in D.returns)
|
||||
})
|
||||
it('Should should deref 8', () => {
|
||||
const T = Type.String({ $id: 'T' })
|
||||
const R = Type.Ref(T)
|
||||
const O = Type.Promise(R)
|
||||
const D = Type.Deref(O, [T])
|
||||
Assert.IsTrue(TypeGuard.IsPromise(D))
|
||||
Assert.IsTrue(TypeGuard.IsString(D.item))
|
||||
Assert.IsFalse('$id' in D.item)
|
||||
})
|
||||
it('Should should deref 9', () => {
|
||||
const T = Type.String({ $id: 'T' })
|
||||
const R1 = Type.Ref(T, { $id: 'R1' })
|
||||
const R2 = Type.Ref(R1, { $id: 'R2' })
|
||||
const R3 = Type.Ref(R2, { $id: 'R3' })
|
||||
const R4 = Type.Ref(R3, { $id: 'R4' })
|
||||
const R5 = Type.Ref(R4, { $id: 'R5' })
|
||||
const R6 = Type.Ref(R5, { $id: 'R6' })
|
||||
const O = Type.Array(R6)
|
||||
const D = Type.Deref(O, [R6, R5, R4, R3, R2, R1, T])
|
||||
Assert.IsTrue(TypeGuard.IsArray(D))
|
||||
Assert.IsTrue(TypeGuard.IsString(D.items))
|
||||
Assert.IsFalse('$id' in D.items)
|
||||
})
|
||||
it('Should should deref 10', () => {
|
||||
const T = Type.String({ $id: 'T' })
|
||||
const R1 = Type.Ref(T, { $id: 'R1' })
|
||||
const R2 = Type.Ref(R1, { $id: 'R2' })
|
||||
const R3 = Type.Ref(R2, { $id: 'R3' })
|
||||
const R4 = Type.Ref(R3, { $id: 'R4' })
|
||||
const R5 = Type.Ref(R4, { $id: 'R5' })
|
||||
const R6 = Type.Ref(R5, { $id: 'R6' })
|
||||
const O = Type.Array(R6)
|
||||
Assert.Throws(() => Type.Deref(O, [R6, R5, R4, R3, R2, R1])) // Omit T
|
||||
})
|
||||
})
|
||||
15
test/runtime/type/guard/type/import.ts
Normal file
15
test/runtime/type/guard/type/import.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { TypeGuard } from '@sinclair/typebox'
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Assert } from '../../../assert/index'
|
||||
|
||||
describe('guard/type/TImport', () => {
|
||||
it('Should guard for TImport', () => {
|
||||
const M = Type.Module({
|
||||
A: Type.String(),
|
||||
})
|
||||
const I = M.Import('A')
|
||||
const N = I.$defs[I.$ref]
|
||||
Assert.IsTrue(TypeGuard.IsImport(I))
|
||||
Assert.IsTrue(TypeGuard.IsString(N))
|
||||
})
|
||||
})
|
||||
@@ -9,11 +9,11 @@ import './composite'
|
||||
import './const'
|
||||
import './constructor'
|
||||
import './date'
|
||||
import './deref'
|
||||
import './enum'
|
||||
import './exclude'
|
||||
import './extract'
|
||||
import './function'
|
||||
import './import'
|
||||
import './indexed'
|
||||
import './integer'
|
||||
import './intersect'
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Assert } from '../../../assert/index'
|
||||
describe('guard/type/TRef', () => {
|
||||
it('Should guard for TRef', () => {
|
||||
const T = Type.Number({ $id: 'T' })
|
||||
const R = TypeGuard.IsRef(Type.Ref(T))
|
||||
const R = TypeGuard.IsRef(Type.Ref('T'))
|
||||
Assert.IsTrue(R)
|
||||
})
|
||||
it('Should not guard for TRef', () => {
|
||||
@@ -14,7 +14,7 @@ describe('guard/type/TRef', () => {
|
||||
})
|
||||
it('Should not guard for TRef with invalid $ref', () => {
|
||||
const T = Type.Number({ $id: 'T' })
|
||||
const S = CloneType(Type.Ref(T))
|
||||
const S = CloneType(Type.Ref('T'))
|
||||
// @ts-ignore
|
||||
S.$ref = 1
|
||||
const R = TypeGuard.IsRef(S)
|
||||
|
||||
51
test/runtime/value/cast/import.ts
Normal file
51
test/runtime/value/cast/import.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
import { Value } from '@sinclair/typebox/value'
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Assert } from '../../assert/index'
|
||||
|
||||
describe('value/cast/Import', () => {
|
||||
const T = Type.Module({
|
||||
A: Type.Number(),
|
||||
}).Import('A')
|
||||
|
||||
const E = 0
|
||||
it('Should upcast from string', () => {
|
||||
const value = 'world'
|
||||
const result = Value.Cast(T, value)
|
||||
Assert.IsEqual(result, E)
|
||||
})
|
||||
it('Should upcast from number', () => {
|
||||
const value = 1
|
||||
const result = Value.Cast(T, value)
|
||||
Assert.IsEqual(result, 1)
|
||||
})
|
||||
it('Should upcast from object', () => {
|
||||
const value = {}
|
||||
const result = Value.Cast(T, value)
|
||||
Assert.IsEqual(result, E)
|
||||
})
|
||||
it('Should upcast from array', () => {
|
||||
const value = [1]
|
||||
const result = Value.Cast(T, value)
|
||||
Assert.IsEqual(result, E)
|
||||
})
|
||||
it('Should upcast from undefined', () => {
|
||||
const value = undefined
|
||||
const result = Value.Cast(T, value)
|
||||
Assert.IsEqual(result, E)
|
||||
})
|
||||
it('Should upcast from null', () => {
|
||||
const value = null
|
||||
const result = Value.Cast(T, value)
|
||||
Assert.IsEqual(result, E)
|
||||
})
|
||||
it('Should upcast from date', () => {
|
||||
const value = new Date(100)
|
||||
const result = Value.Cast(T, value)
|
||||
Assert.IsEqual(result, E)
|
||||
})
|
||||
it('Should preseve', () => {
|
||||
const value = 123
|
||||
const result = Value.Cast(T, value)
|
||||
Assert.IsEqual(result, 123)
|
||||
})
|
||||
})
|
||||
@@ -6,6 +6,7 @@ import './boolean'
|
||||
import './composite'
|
||||
import './date'
|
||||
import './enum'
|
||||
import './import'
|
||||
import './integer'
|
||||
import './intersect'
|
||||
import './iterator'
|
||||
|
||||
@@ -153,7 +153,7 @@ describe('value/cast/Union', () => {
|
||||
const A = Type.Object({ type: Type.Literal('A') }, { $id: 'A' })
|
||||
const B = Type.Object({ type: Type.Literal('B'), value: Type.Number() }, { $id: 'B' })
|
||||
const RA = Type.Union([A, B])
|
||||
const RB = Type.Union([Type.Ref(A), Type.Ref(B)])
|
||||
const RB = Type.Union([Type.Ref('A'), Type.Ref('B')])
|
||||
// variant 0
|
||||
Assert.IsEqual(Value.Cast(RA, [A, B], { type: 'B' }), { type: 'B', value: 0 })
|
||||
Assert.IsEqual(Value.Cast(RB, [A, B], { type: 'B' }), { type: 'B', value: 0 })
|
||||
|
||||
@@ -15,6 +15,7 @@ import './iterator'
|
||||
import './keyof'
|
||||
import './kind'
|
||||
import './literal'
|
||||
import './module'
|
||||
import './never'
|
||||
import './not'
|
||||
import './null'
|
||||
|
||||
80
test/runtime/value/check/module.ts
Normal file
80
test/runtime/value/check/module.ts
Normal file
@@ -0,0 +1,80 @@
|
||||
import { Value } from '@sinclair/typebox/value'
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Assert } from '../../assert'
|
||||
|
||||
describe('value/check/Module', () => {
|
||||
it('Should validate string', () => {
|
||||
const Module = Type.Module({
|
||||
A: Type.String(),
|
||||
})
|
||||
const T = Module.Import('A')
|
||||
Assert.IsTrue(Value.Check(T, 'hello'))
|
||||
Assert.IsFalse(Value.Check(T, true))
|
||||
})
|
||||
it('Should validate referenced string', () => {
|
||||
const Module = Type.Module({
|
||||
A: Type.String(),
|
||||
B: Type.Ref('A'),
|
||||
})
|
||||
const T = Module.Import('B')
|
||||
Assert.IsTrue(Value.Check(T, 'hello'))
|
||||
Assert.IsFalse(Value.Check(T, true))
|
||||
})
|
||||
it('Should validate self referential', () => {
|
||||
const Module = Type.Module({
|
||||
A: Type.Object({
|
||||
nodes: Type.Array(Type.Ref('A')),
|
||||
}),
|
||||
})
|
||||
const T = Module.Import('A')
|
||||
Assert.IsTrue(Value.Check(T, { nodes: [{ nodes: [{ nodes: [] }, { nodes: [] }] }] }))
|
||||
Assert.IsFalse(Value.Check(T, { nodes: [{ nodes: [{ nodes: [] }, { nodes: false }] }] }))
|
||||
Assert.IsFalse(Value.Check(T, true))
|
||||
})
|
||||
it('Should validate mutual recursive', () => {
|
||||
const Module = Type.Module({
|
||||
A: Type.Object({
|
||||
b: Type.Ref('B'),
|
||||
}),
|
||||
B: Type.Object({
|
||||
a: Type.Union([Type.Ref('A'), Type.Null()]),
|
||||
}),
|
||||
})
|
||||
const T = Module.Import('A')
|
||||
Assert.IsTrue(Value.Check(T, { b: { a: null } }))
|
||||
Assert.IsTrue(Value.Check(T, { b: { a: { b: { a: null } } } }))
|
||||
|
||||
Assert.IsFalse(Value.Check(T, { b: { a: 1 } }))
|
||||
Assert.IsFalse(Value.Check(T, { b: { a: { b: { a: 1 } } } }))
|
||||
Assert.IsFalse(Value.Check(T, true))
|
||||
})
|
||||
it('Should validate mutual recursive (Array)', () => {
|
||||
const Module = Type.Module({
|
||||
A: Type.Object({
|
||||
b: Type.Ref('B'),
|
||||
}),
|
||||
B: Type.Object({
|
||||
a: Type.Array(Type.Ref('A')),
|
||||
}),
|
||||
})
|
||||
const T = Module.Import('A')
|
||||
Assert.IsTrue(Value.Check(T, { b: { a: [{ b: { a: [] } }] } }))
|
||||
Assert.IsFalse(Value.Check(T, { b: { a: [{ b: { a: [null] } }] } }))
|
||||
Assert.IsFalse(Value.Check(T, true))
|
||||
})
|
||||
it('Should validate deep referential', () => {
|
||||
const Module = Type.Module({
|
||||
A: Type.Ref('B'),
|
||||
B: Type.Ref('C'),
|
||||
C: Type.Ref('D'),
|
||||
D: Type.Ref('E'),
|
||||
E: Type.Ref('F'),
|
||||
F: Type.Ref('G'),
|
||||
G: Type.Ref('H'),
|
||||
H: Type.Literal('hello'),
|
||||
})
|
||||
const T = Module.Import('A')
|
||||
Assert.IsTrue(Value.Check(T, 'hello'))
|
||||
Assert.IsFalse(Value.Check(T, 'world'))
|
||||
})
|
||||
})
|
||||
@@ -12,7 +12,7 @@ describe('value/check/Ref', () => {
|
||||
},
|
||||
{ $id: Assert.NextId() },
|
||||
)
|
||||
const R = Type.Ref(T)
|
||||
const R = Type.Ref(T.$id!)
|
||||
Assert.IsEqual(
|
||||
Value.Check(R, [T], {
|
||||
x: 1,
|
||||
@@ -32,7 +32,7 @@ describe('value/check/Ref', () => {
|
||||
},
|
||||
{ $id: Assert.NextId() },
|
||||
)
|
||||
const R = Type.Ref(T)
|
||||
const R = Type.Ref(T.$id!)
|
||||
Assert.IsEqual(
|
||||
Value.Check(R, [T], {
|
||||
x: 1,
|
||||
@@ -55,7 +55,7 @@ describe('value/check/Ref', () => {
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
z: Type.Number(),
|
||||
r: Type.Optional(Type.Ref(T)),
|
||||
r: Type.Optional(Type.Ref(T.$id!)),
|
||||
},
|
||||
{ $id: 'T' },
|
||||
)
|
||||
@@ -76,7 +76,7 @@ describe('value/check/Ref', () => {
|
||||
nodes: Type.Array(Node),
|
||||
}),
|
||||
)
|
||||
const R = Type.Ref(T)
|
||||
const R = Type.Ref(T.$id!)
|
||||
Assert.IsEqual(Value.Check(R, [T], { id: '', nodes: [{ id: '', nodes: [] }] }), true)
|
||||
Assert.IsEqual(Value.Check(R, [T], { id: '', nodes: [{ id: 1, nodes: [] }] }), false)
|
||||
})
|
||||
|
||||
203
test/runtime/value/clean/import.ts
Normal file
203
test/runtime/value/clean/import.ts
Normal file
@@ -0,0 +1,203 @@
|
||||
import { Value } from '@sinclair/typebox/value'
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Assert } from '../../assert/index'
|
||||
|
||||
describe('value/clean/Import', () => {
|
||||
// ----------------------------------------------------------------
|
||||
// Clean
|
||||
// ----------------------------------------------------------------
|
||||
it('Should clean 1', () => {
|
||||
const T = Type.Module({ A: Type.Object({ x: Type.Number() }) }).Import('A')
|
||||
const R = Value.Clean(T, null)
|
||||
Assert.IsEqual(R, null)
|
||||
})
|
||||
it('Should clean 2', () => {
|
||||
const T = Type.Module({ A: Type.Object({ x: Type.Number() }) }).Import('A')
|
||||
const R = Value.Clean(T, {})
|
||||
Assert.IsEqual(R, {})
|
||||
})
|
||||
it('Should clean 3', () => {
|
||||
const T = Type.Module({ A: Type.Object({ x: Type.Number() }) }).Import('A')
|
||||
const R = Value.Clean(T, { x: 1 })
|
||||
Assert.IsEqual(R, { x: 1 })
|
||||
})
|
||||
it('Should clean 4', () => {
|
||||
const T = Type.Module({ A: Type.Object({ x: Type.Number() }) }).Import('A')
|
||||
const R = Value.Clean(T, { x: null })
|
||||
Assert.IsEqual(R, { x: null })
|
||||
})
|
||||
// ----------------------------------------------------------------
|
||||
// Nested
|
||||
// ----------------------------------------------------------------
|
||||
it('Should clean nested 1', () => {
|
||||
const T = Type.Module({
|
||||
A: Type.Object({
|
||||
x: Type.Object({
|
||||
y: Type.Number(),
|
||||
}),
|
||||
}),
|
||||
}).Import('A')
|
||||
const R = Value.Clean(T, null)
|
||||
Assert.IsEqual(R, null)
|
||||
})
|
||||
it('Should clean nested 2', () => {
|
||||
const T = Type.Module({
|
||||
A: Type.Object({
|
||||
x: Type.Object({
|
||||
y: Type.Number(),
|
||||
}),
|
||||
}),
|
||||
}).Import('A')
|
||||
const R = Value.Clean(T, {})
|
||||
Assert.IsEqual(R, {})
|
||||
})
|
||||
it('Should clean nested 3', () => {
|
||||
const T = Type.Module({
|
||||
A: Type.Object({
|
||||
x: Type.Object({
|
||||
y: Type.Number(),
|
||||
}),
|
||||
}),
|
||||
}).Import('A')
|
||||
|
||||
const R = Value.Clean(T, { x: null })
|
||||
Assert.IsEqual(R, { x: null })
|
||||
})
|
||||
it('Should clean nested 4', () => {
|
||||
const T = Type.Module({
|
||||
A: Type.Object({
|
||||
x: Type.Object({
|
||||
y: Type.Number(),
|
||||
}),
|
||||
}),
|
||||
}).Import('A')
|
||||
const R = Value.Clean(T, { x: { y: null } })
|
||||
Assert.IsEqual(R, { x: { y: null } })
|
||||
})
|
||||
// ----------------------------------------------------------------
|
||||
// Additional Properties
|
||||
// ----------------------------------------------------------------
|
||||
it('Should clean additional properties 1', () => {
|
||||
const T = Type.Module({
|
||||
A: Type.Object(
|
||||
{
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
},
|
||||
{
|
||||
additionalProperties: Type.String(),
|
||||
},
|
||||
),
|
||||
}).Import('A')
|
||||
const R = Value.Clean(T, null)
|
||||
Assert.IsEqual(R, null)
|
||||
})
|
||||
it('Should clean additional properties 2', () => {
|
||||
const T = Type.Module({
|
||||
A: Type.Object(
|
||||
{
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
},
|
||||
{
|
||||
additionalProperties: Type.String(),
|
||||
},
|
||||
),
|
||||
}).Import('A')
|
||||
const R = Value.Clean(T, {})
|
||||
Assert.IsEqual(R, {})
|
||||
})
|
||||
it('Should clean additional properties 3', () => {
|
||||
const T = Type.Module({
|
||||
A: Type.Object(
|
||||
{
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
},
|
||||
{
|
||||
additionalProperties: Type.String(),
|
||||
},
|
||||
),
|
||||
}).Import('A')
|
||||
const R = Value.Clean(T, { x: 1 })
|
||||
Assert.IsEqual(R, { x: 1 })
|
||||
})
|
||||
it('Should clean additional properties 4', () => {
|
||||
const T = Type.Module({
|
||||
A: Type.Object(
|
||||
{
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
},
|
||||
{
|
||||
additionalProperties: Type.String(),
|
||||
},
|
||||
),
|
||||
}).Import('A')
|
||||
const R = Value.Clean(T, { x: 1, y: 2 })
|
||||
Assert.IsEqual(R, { x: 1, y: 2 })
|
||||
})
|
||||
// ----------------------------------------------------------------
|
||||
// Additional Properties Discard
|
||||
// ----------------------------------------------------------------
|
||||
it('Should clean additional properties discard 1', () => {
|
||||
const T = Type.Module({
|
||||
A: Type.Object(
|
||||
{
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
},
|
||||
{
|
||||
additionalProperties: Type.String(),
|
||||
},
|
||||
),
|
||||
}).Import('A')
|
||||
const R = Value.Clean(T, null)
|
||||
Assert.IsEqual(R, null)
|
||||
})
|
||||
it('Should clean additional properties discard 2', () => {
|
||||
const T = Type.Module({
|
||||
A: Type.Object(
|
||||
{
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
},
|
||||
{
|
||||
additionalProperties: Type.String(),
|
||||
},
|
||||
),
|
||||
}).Import('A')
|
||||
const R = Value.Clean(T, { k: '', d: null })
|
||||
Assert.IsEqual(R, { k: '' })
|
||||
})
|
||||
it('Should clean additional properties discard 3', () => {
|
||||
const T = Type.Module({
|
||||
A: Type.Object(
|
||||
{
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
},
|
||||
{
|
||||
additionalProperties: Type.String(),
|
||||
},
|
||||
),
|
||||
}).Import('A')
|
||||
const R = Value.Clean(T, { k: '', d: null, x: 1 })
|
||||
Assert.IsEqual(R, { k: '', x: 1 })
|
||||
})
|
||||
it('Should clean additional properties discard 4', () => {
|
||||
const T = Type.Module({
|
||||
A: Type.Object(
|
||||
{
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
},
|
||||
{
|
||||
additionalProperties: Type.String(),
|
||||
},
|
||||
),
|
||||
}).Import('A')
|
||||
const R = Value.Clean(T, { k: '', d: null, x: 1, y: 2 })
|
||||
Assert.IsEqual(R, { k: '', x: 1, y: 2 })
|
||||
})
|
||||
})
|
||||
@@ -8,6 +8,7 @@ import './constructor'
|
||||
import './date'
|
||||
import './enum'
|
||||
import './function'
|
||||
import './import'
|
||||
import './integer'
|
||||
import './intersect'
|
||||
import './iterator'
|
||||
|
||||
29
test/runtime/value/convert/import.ts
Normal file
29
test/runtime/value/convert/import.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { Value } from '@sinclair/typebox/value'
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Assert } from '../../assert/index'
|
||||
|
||||
// prettier-ignore
|
||||
describe('value/convert/Import', () => {
|
||||
it('Should convert properties', () => {
|
||||
const T = Type.Module({ A: Type.Object({
|
||||
x: Type.Number(),
|
||||
y: Type.Boolean(),
|
||||
z: Type.Boolean()
|
||||
})}).Import('A')
|
||||
const R = Value.Convert(T, { x: '42', y: 'true', z: 'hello' })
|
||||
Assert.IsEqual(R, { x: 42, y: true, z: 'hello' })
|
||||
})
|
||||
it('Should convert known properties', () => {
|
||||
const T = Type.Module({ A: Type.Object({
|
||||
x: Type.Number(),
|
||||
y: Type.Boolean()
|
||||
})}).Import('A')
|
||||
const R = Value.Convert(T, { x: '42', y: 'true', z: 'hello' })
|
||||
Assert.IsEqual(R, { x: 42, y: true, z: 'hello' })
|
||||
})
|
||||
it('Should not convert missing properties', () => {
|
||||
const T = Type.Module({ A: Type.Object({ x: Type.Number() }) }).Import('A')
|
||||
const R = Value.Convert(T, { })
|
||||
Assert.IsEqual(R, { })
|
||||
})
|
||||
})
|
||||
@@ -9,6 +9,7 @@ import './kind'
|
||||
import './date'
|
||||
import './enum'
|
||||
import './function'
|
||||
import './import'
|
||||
import './integer'
|
||||
import './intersect'
|
||||
import './iterator'
|
||||
|
||||
98
test/runtime/value/create/import.ts
Normal file
98
test/runtime/value/create/import.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
import { Value } from '@sinclair/typebox/value'
|
||||
import { Type } from '@sinclair/typebox'
|
||||
import { Assert } from '../../assert/index'
|
||||
|
||||
describe('value/create/Import', () => {
|
||||
it('Should create value', () => {
|
||||
const T = Type.Module({
|
||||
A: Type.Object({
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
z: Type.Number(),
|
||||
}),
|
||||
}).Import('A')
|
||||
Assert.IsEqual(Value.Create(T), {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0,
|
||||
})
|
||||
})
|
||||
it('Should create value with optional properties', () => {
|
||||
const T = Type.Module({
|
||||
A: Type.Object({
|
||||
x: Type.Optional(Type.Number()),
|
||||
y: Type.Optional(Type.Number()),
|
||||
z: Type.Optional(Type.Number()),
|
||||
}),
|
||||
}).Import('A')
|
||||
Assert.IsEqual(Value.Create(T), {})
|
||||
})
|
||||
it('Should create default with default properties', () => {
|
||||
const T = Type.Module({
|
||||
A: Type.Object({
|
||||
x: Type.Number({ default: 1 }),
|
||||
y: Type.Number({ default: 2 }),
|
||||
z: Type.Number({ default: 3 }),
|
||||
}),
|
||||
}).Import('A')
|
||||
Assert.IsEqual(Value.Create(T), {
|
||||
x: 1,
|
||||
y: 2,
|
||||
z: 3,
|
||||
})
|
||||
})
|
||||
it('Should create nested object', () => {
|
||||
const T = Type.Module({
|
||||
A: Type.Object({
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
z: Type.Number(),
|
||||
w: Type.Object({
|
||||
x: Type.Number({ default: 7 }),
|
||||
y: Type.Number(),
|
||||
z: Type.Number(),
|
||||
}),
|
||||
}),
|
||||
}).Import('A')
|
||||
Assert.IsEqual(Value.Create(T), {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0,
|
||||
w: { x: 7, y: 0, z: 0 },
|
||||
})
|
||||
})
|
||||
it('Should create with default', () => {
|
||||
const T = Type.Module({
|
||||
A: Type.Object(
|
||||
{
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
z: Type.Number(),
|
||||
},
|
||||
{ default: { x: 1, y: 2, z: 3 } },
|
||||
),
|
||||
}).Import('A')
|
||||
Assert.IsEqual(Value.Create(T), {
|
||||
x: 1,
|
||||
y: 2,
|
||||
z: 3,
|
||||
})
|
||||
})
|
||||
// ----------------------------------------------------------------
|
||||
// Mutation
|
||||
// ----------------------------------------------------------------
|
||||
// https://github.com/sinclairzx81/typebox/issues/726
|
||||
it('Should clone defaults on assignment - no mutation', () => {
|
||||
const T = Type.Module({
|
||||
A: Type.Object(
|
||||
{
|
||||
x: Type.Number(),
|
||||
},
|
||||
{ default: { x: 1 } },
|
||||
),
|
||||
}).Import('A')
|
||||
const V = Value.Create(T)
|
||||
V.x = 123
|
||||
Assert.IsEqual(T.$defs.A.default, { x: 1 })
|
||||
})
|
||||
})
|
||||
@@ -9,6 +9,7 @@ import './constructor'
|
||||
import './date'
|
||||
import './enum'
|
||||
import './function'
|
||||
import './import'
|
||||
import './integer'
|
||||
import './intersect'
|
||||
import './iterator'
|
||||
|
||||
@@ -12,7 +12,7 @@ describe('value/create/Ref', () => {
|
||||
},
|
||||
{ $id: 'T', default: 'target' },
|
||||
)
|
||||
const R = Type.Ref(T)
|
||||
const R = Type.Ref('T')
|
||||
Assert.Throws(() => Value.Create(R))
|
||||
})
|
||||
it('Should create ref default if ref default is defined', () => {
|
||||
@@ -24,12 +24,12 @@ describe('value/create/Ref', () => {
|
||||
},
|
||||
{ $id: 'T', default: 'target' },
|
||||
)
|
||||
const R = Type.Ref(T, { default: 'override' })
|
||||
const R = Type.Ref('T', { default: 'override' })
|
||||
Assert.IsEqual(Value.Create(R), 'override') // terminated at R default value
|
||||
})
|
||||
it('Should dereference remote schema via $ref', () => {
|
||||
const R = Type.Number({ $id: 'S' })
|
||||
const T = Type.Object({ x: Type.Ref(R) })
|
||||
const R = Type.Number({ $id: 'R' })
|
||||
const T = Type.Object({ x: Type.Ref('R') })
|
||||
Assert.IsEqual(Value.Create(T, [R]), { x: 0 })
|
||||
})
|
||||
})
|
||||
|
||||
299
test/runtime/value/default/import.ts
Normal file
299
test/runtime/value/default/import.ts
Normal file
@@ -0,0 +1,299 @@
|
||||
import { Value } from '@sinclair/typebox/value'
|
||||
import { Type, CloneType } from '@sinclair/typebox'
|
||||
import { Assert } from '../../assert/index'
|
||||
|
||||
describe('value/default/Import', () => {
|
||||
it('Should use default', () => {
|
||||
const T = Type.Module({
|
||||
A: Type.Object(
|
||||
{
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
},
|
||||
{ default: 1 },
|
||||
),
|
||||
}).Import('A')
|
||||
const R = Value.Default(T, undefined)
|
||||
Assert.IsEqual(R, 1)
|
||||
})
|
||||
it('Should use value', () => {
|
||||
const T = Type.Module({
|
||||
A: Type.Object(
|
||||
{
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
},
|
||||
{ default: 1 },
|
||||
),
|
||||
}).Import('A')
|
||||
const R = Value.Default(T, null)
|
||||
Assert.IsEqual(R, null)
|
||||
})
|
||||
// ----------------------------------------------------------------
|
||||
// Construction
|
||||
// ----------------------------------------------------------------
|
||||
it('Should should fully construct object 1', () => {
|
||||
const T = Type.Module({
|
||||
A: Type.Object(
|
||||
{
|
||||
x: Type.Object(
|
||||
{
|
||||
x: Type.Number({ default: 1 }),
|
||||
y: Type.Number({ default: 2 }),
|
||||
},
|
||||
{ default: {} },
|
||||
),
|
||||
y: Type.Object(
|
||||
{
|
||||
x: Type.Number({ default: 3 }),
|
||||
y: Type.Number({ default: 4 }),
|
||||
},
|
||||
{ default: {} },
|
||||
),
|
||||
},
|
||||
{ default: {} },
|
||||
),
|
||||
}).Import('A')
|
||||
const R = Value.Default(T, undefined)
|
||||
Assert.IsEqual(R, { x: { x: 1, y: 2 }, y: { x: 3, y: 4 } })
|
||||
})
|
||||
it('Should should fully construct object 2', () => {
|
||||
const T = Type.Module({
|
||||
A: Type.Object(
|
||||
{
|
||||
x: Type.Object(
|
||||
{
|
||||
x: Type.Number({ default: 1 }),
|
||||
y: Type.Number({ default: 2 }),
|
||||
},
|
||||
{ default: {} },
|
||||
),
|
||||
y: Type.Object(
|
||||
{
|
||||
x: Type.Number({ default: 3 }),
|
||||
y: Type.Number({ default: 4 }),
|
||||
},
|
||||
{ default: {} },
|
||||
),
|
||||
},
|
||||
{ default: {} },
|
||||
),
|
||||
}).Import('A')
|
||||
const R = Value.Default(T, { x: null })
|
||||
Assert.IsEqual(R, { x: null, y: { x: 3, y: 4 } })
|
||||
})
|
||||
it('Should should fully construct object 3', () => {
|
||||
const T = Type.Module({
|
||||
A: Type.Object(
|
||||
{
|
||||
x: Type.Object(
|
||||
{
|
||||
x: Type.Number({ default: 1 }),
|
||||
y: Type.Number({ default: 2 }),
|
||||
},
|
||||
{ default: {} },
|
||||
),
|
||||
y: Type.Object(
|
||||
{
|
||||
x: Type.Number({ default: 3 }),
|
||||
y: Type.Number({ default: 4 }),
|
||||
},
|
||||
{ default: {} },
|
||||
),
|
||||
},
|
||||
{ default: {} },
|
||||
),
|
||||
}).Import('A')
|
||||
const R = Value.Default(T, { x: { x: null, y: null } })
|
||||
Assert.IsEqual(R, { x: { x: null, y: null }, y: { x: 3, y: 4 } })
|
||||
})
|
||||
// ----------------------------------------------------------------
|
||||
// Properties
|
||||
// ----------------------------------------------------------------
|
||||
it('Should use property defaults 1', () => {
|
||||
const T = Type.Module({
|
||||
A: Type.Object(
|
||||
{
|
||||
x: Type.Number({ default: 1 }),
|
||||
y: Type.Number({ default: 2 }),
|
||||
},
|
||||
{ default: 1 },
|
||||
),
|
||||
}).Import('A')
|
||||
const R = Value.Default(T, {})
|
||||
Assert.IsEqual(R, { x: 1, y: 2 })
|
||||
})
|
||||
it('Should use property defaults 2', () => {
|
||||
const T = Type.Module({
|
||||
A: Type.Object({
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
}),
|
||||
}).Import('A')
|
||||
const R = Value.Default(T, {})
|
||||
Assert.IsEqual(R, {})
|
||||
})
|
||||
it('Should use property defaults 3', () => {
|
||||
const T = Type.Module({
|
||||
A: Type.Object({
|
||||
x: Type.Number({ default: 1 }),
|
||||
y: Type.Number(),
|
||||
}),
|
||||
}).Import('A')
|
||||
const R = Value.Default(T, {})
|
||||
Assert.IsEqual(R, { x: 1 })
|
||||
})
|
||||
it('Should use property defaults 4', () => {
|
||||
const T = Type.Module({
|
||||
A: Type.Object({
|
||||
x: Type.Number({ default: 1 }),
|
||||
y: Type.Number(),
|
||||
}),
|
||||
}).Import('A')
|
||||
const R = Value.Default(T, { x: 3 })
|
||||
Assert.IsEqual(R, { x: 3 })
|
||||
})
|
||||
// ----------------------------------------------------------------
|
||||
// AdditionalProperties
|
||||
// ----------------------------------------------------------------
|
||||
it('Should use additional property defaults 1', () => {
|
||||
const T = Type.Module({
|
||||
A: Type.Object(
|
||||
{
|
||||
x: Type.Number({ default: 1 }),
|
||||
y: Type.Number({ default: 2 }),
|
||||
},
|
||||
{
|
||||
additionalProperties: Type.Number({ default: 3 }),
|
||||
},
|
||||
),
|
||||
}).Import('A')
|
||||
const R = Value.Default(T, {})
|
||||
Assert.IsEqual(R, { x: 1, y: 2 })
|
||||
})
|
||||
it('Should use additional property defaults 2', () => {
|
||||
const T = Type.Module({
|
||||
A: Type.Object(
|
||||
{
|
||||
x: Type.Number({ default: 1 }),
|
||||
y: Type.Number({ default: 2 }),
|
||||
},
|
||||
{
|
||||
additionalProperties: Type.Number({ default: 3 }),
|
||||
},
|
||||
),
|
||||
}).Import('A')
|
||||
const R = Value.Default(T, { x: null, y: null, z: undefined })
|
||||
Assert.IsEqual(R, { x: null, y: null, z: 3 })
|
||||
})
|
||||
it('Should use additional property defaults 3', () => {
|
||||
const T = Type.Module({
|
||||
A: Type.Object(
|
||||
{
|
||||
x: Type.Number({ default: 1 }),
|
||||
y: Type.Number({ default: 2 }),
|
||||
},
|
||||
{
|
||||
additionalProperties: Type.Number(),
|
||||
},
|
||||
),
|
||||
}).Import('A')
|
||||
const R = Value.Default(T, { x: null, y: null, z: undefined })
|
||||
Assert.IsEqual(R, { x: null, y: null, z: undefined })
|
||||
})
|
||||
// ----------------------------------------------------------------
|
||||
// Mutation
|
||||
// ----------------------------------------------------------------
|
||||
// https://github.com/sinclairzx81/typebox/issues/726
|
||||
it('Should retain defaults on operation', () => {
|
||||
const A = Type.Module({
|
||||
A: Type.Object({
|
||||
a: Type.Object(
|
||||
{
|
||||
b: Type.Array(Type.String(), { default: [] }),
|
||||
},
|
||||
{ default: {} },
|
||||
),
|
||||
}),
|
||||
}).Import('A')
|
||||
const value = Value.Default(A, {})
|
||||
Assert.IsEqual(value, { a: { b: [] } })
|
||||
Assert.IsEqual(A.$defs.A.properties.a.default, {})
|
||||
Assert.IsEqual(A.$defs.A.properties.a.properties.b.default, [])
|
||||
})
|
||||
// https://github.com/sinclairzx81/typebox/issues/726
|
||||
it('Should retain schematics on operation', () => {
|
||||
const A = Type.Module({
|
||||
A: Type.Object({
|
||||
a: Type.Object(
|
||||
{
|
||||
b: Type.Array(Type.String(), { default: [] }),
|
||||
},
|
||||
{ default: {} },
|
||||
),
|
||||
}),
|
||||
}).Import('A')
|
||||
const B = CloneType(A)
|
||||
Value.Default(A, {})
|
||||
Assert.IsEqual(A, B)
|
||||
})
|
||||
// ----------------------------------------------------------------
|
||||
// Traveral: https://github.com/sinclairzx81/typebox/issues/962
|
||||
// ----------------------------------------------------------------
|
||||
it('Should traverse into an object 1 (initialize)', () => {
|
||||
const M = Type.Module({
|
||||
Y: Type.Object({ y: Type.String({ default: 'y' }) }),
|
||||
X: Type.Object({ x: Type.Ref('Y') }),
|
||||
})
|
||||
const Y = M.Import('Y')
|
||||
const X = M.Import('X')
|
||||
Assert.IsEqual(Value.Default(Y, {}), { y: 'y' })
|
||||
Assert.IsEqual(Value.Default(X, { x: {} }), { x: { y: 'y' } })
|
||||
})
|
||||
it('Should traverse into an object 2 (retain)', () => {
|
||||
const M = Type.Module({
|
||||
Y: Type.Object({ y: Type.String({ default: 'y' }) }),
|
||||
X: Type.Object({ x: Type.Ref('Y') }),
|
||||
})
|
||||
const Y = M.Import('Y')
|
||||
const X = M.Import('X')
|
||||
Assert.IsEqual(Value.Default(X, { x: { y: 1 } }), { x: { y: 1 } })
|
||||
})
|
||||
it('Should traverse into an object 3 (ignore on undefined)', () => {
|
||||
const M = Type.Module({
|
||||
Y: Type.Object({ y: Type.String({ default: 'y' }) }),
|
||||
X: Type.Object({ x: Type.Ref('Y') }),
|
||||
})
|
||||
const Y = M.Import('Y')
|
||||
const X = M.Import('X')
|
||||
Assert.IsEqual(Value.Default(X, { x: undefined }), { x: undefined })
|
||||
})
|
||||
// ----------------------------------------------------------------
|
||||
// Exterior Object Defaults
|
||||
// ----------------------------------------------------------------
|
||||
it('Should default exterior into an object 1', () => {
|
||||
const X = Type.Module({ A: Type.Object({ x: Type.String({ default: 1 }) }, { default: {} }) }).Import('A')
|
||||
const R = Value.Default(X, undefined)
|
||||
Assert.IsEqual(R, { x: 1 })
|
||||
})
|
||||
it('Should default exterior into an object 2', () => {
|
||||
const X = Type.Module({ A: Type.Object({ x: Type.String({ default: 1 }) }, { default: {} }) }).Import('A')
|
||||
const R = Value.Default(X, {})
|
||||
Assert.IsEqual(R, { x: 1 })
|
||||
})
|
||||
it('Should default exterior into an object 3', () => {
|
||||
const X = Type.Module({ A: Type.Object({ x: Type.String({ default: 1 }) }, { default: {} }) }).Import('A')
|
||||
const R = Value.Default(X, { y: 3 })
|
||||
Assert.IsEqual(R, { y: 3, x: 1 })
|
||||
})
|
||||
it('Should default exterior into an object 4', () => {
|
||||
const X = Type.Module({ A: Type.Object({ x: Type.String({ default: 1 }) }, { default: {} }) }).Import('A')
|
||||
const R = Value.Default(X, { y: 3, x: 7 })
|
||||
Assert.IsEqual(R, { y: 3, x: 7 })
|
||||
})
|
||||
it('Should default exterior into an object 5', () => {
|
||||
const X = Type.Module({ A: Type.Object({ x: Type.String({ default: 1 }) }, { default: {} }) }).Import('A')
|
||||
const R = Value.Default(X, { x: 2 })
|
||||
Assert.IsEqual(R, { x: 2 })
|
||||
})
|
||||
})
|
||||
@@ -9,6 +9,7 @@ import './constructor'
|
||||
import './date'
|
||||
import './enum'
|
||||
import './function'
|
||||
import './import'
|
||||
import './integer'
|
||||
import './intersect'
|
||||
import './iterator'
|
||||
|
||||
157
test/runtime/value/transform/import.ts
Normal file
157
test/runtime/value/transform/import.ts
Normal file
@@ -0,0 +1,157 @@
|
||||
import * as Encoder from './_encoder'
|
||||
import { Assert } from '../../assert'
|
||||
|
||||
import { TypeSystemPolicy } from '@sinclair/typebox/system'
|
||||
import { Value } from '@sinclair/typebox/value'
|
||||
import { Type } from '@sinclair/typebox'
|
||||
|
||||
// prettier-ignore
|
||||
describe('value/transform/Import', () => {
|
||||
// --------------------------------------------------------
|
||||
// Identity
|
||||
// --------------------------------------------------------
|
||||
const T0 = Type.Transform(
|
||||
Type.Module({ A: Type.Object({
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
})}).Import('A'),
|
||||
)
|
||||
.Decode((value) => value)
|
||||
.Encode((value) => value)
|
||||
it('Should decode identity', () => {
|
||||
const R = Encoder.Decode(T0, { x: 1, y: 2 })
|
||||
Assert.IsEqual(R, { x: 1, y: 2 })
|
||||
})
|
||||
it('Should encode identity', () => {
|
||||
const R = Encoder.Encode(T0, { x: 1, y: 2 })
|
||||
Assert.IsEqual(R, { x: 1, y: 2 })
|
||||
})
|
||||
it('Should throw on identity decode', () => {
|
||||
Assert.Throws(() => Encoder.Decode(T0, undefined))
|
||||
})
|
||||
// ----------------------------------------------------------
|
||||
// Object
|
||||
// ----------------------------------------------------------
|
||||
const T1 = Type.Transform(
|
||||
Type.Module({ A: Type.Object({
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
})}).Import('A'),
|
||||
)
|
||||
.Decode((value) => 42)
|
||||
.Encode((value) => ({ x: 1, y: 2 }))
|
||||
it('Should decode mapped', () => {
|
||||
const R = Encoder.Decode(T1, { x: 1, y: 2 })
|
||||
Assert.IsEqual(R, 42)
|
||||
})
|
||||
it('Should encode mapped', () => {
|
||||
const R = Encoder.Encode(T1, null)
|
||||
Assert.IsEqual(R, { x: 1, y: 2 })
|
||||
})
|
||||
it('Should throw on mapped decode', () => {
|
||||
Assert.Throws(() => Encoder.Decode(T1, undefined))
|
||||
})
|
||||
// ----------------------------------------------------------
|
||||
// Object: Transform Property
|
||||
// ----------------------------------------------------------
|
||||
const N2 = Type.Transform(Type.Module({ A: Type.Integer() }).Import('A'))
|
||||
.Decode((value) => value.toString())
|
||||
.Encode((value) => parseInt(value))
|
||||
const T2 = Type.Object({
|
||||
x: N2,
|
||||
y: N2,
|
||||
})
|
||||
it('Should decode transform property', () => {
|
||||
const R = Encoder.Decode(T2, { x: 1, y: 2 })
|
||||
Assert.IsEqual(R, { x: '1', y: '2' })
|
||||
})
|
||||
it('Should encode transform property', () => {
|
||||
const R = Encoder.Encode(T2, { x: '1', y: '2' })
|
||||
Assert.IsEqual(R, { x: 1, y: 2 })
|
||||
})
|
||||
it('Should throw on decode transform property', () => {
|
||||
Assert.Throws(() => Encoder.Decode(T2, undefined))
|
||||
})
|
||||
// ----------------------------------------------------------
|
||||
// Object: Transform Property Nested (Twizzle)
|
||||
// ----------------------------------------------------------
|
||||
const N3 = Type.Transform(Type.Module({ A: Type.Integer() }).Import('A'))
|
||||
.Decode((value) => value.toString())
|
||||
.Encode((value) => parseInt(value))
|
||||
const T3 = Type.Transform(
|
||||
Type.Object({
|
||||
x: N3,
|
||||
y: N3,
|
||||
}),
|
||||
)
|
||||
.Decode((value) => ({ x: value.y, y: value.x }))
|
||||
.Encode((value) => ({ x: value.y, y: value.x }))
|
||||
it('Should decode transform property nested', () => {
|
||||
const R = Encoder.Decode(T3, { x: 1, y: 2 })
|
||||
Assert.IsEqual(R, { x: '2', y: '1' })
|
||||
})
|
||||
it('Should encode transform property nested', () => {
|
||||
const R = Encoder.Encode(T3, { x: '1', y: '2' })
|
||||
Assert.IsEqual(R, { x: 2, y: 1 })
|
||||
})
|
||||
it('Should throw on decode transform property nested', () => {
|
||||
Assert.Throws(() => Encoder.Decode(T3, undefined))
|
||||
})
|
||||
// ----------------------------------------------------------
|
||||
// Object Additional Properties
|
||||
// ----------------------------------------------------------
|
||||
const N4 = Type.Transform(Type.Module({ A: Type.Integer() }).Import('A'))
|
||||
.Decode((value) => value.toString())
|
||||
.Encode((value) => parseInt(value))
|
||||
const T4 = Type.Transform(
|
||||
Type.Object(
|
||||
{
|
||||
x: Type.Number(),
|
||||
},
|
||||
{
|
||||
additionalProperties: N4,
|
||||
},
|
||||
),
|
||||
)
|
||||
.Decode((value) => value)
|
||||
.Encode((value) => value)
|
||||
it('Should decode additional property', () => {
|
||||
const R = Encoder.Decode(T4, { x: 1, y: 2 })
|
||||
Assert.IsEqual(R, { x: 1, y: '2' })
|
||||
})
|
||||
it('Should encode additional property', () => {
|
||||
const R = Encoder.Encode(T4, { x: 1, y: '5' })
|
||||
Assert.IsEqual(R, { x: 1, y: 5 })
|
||||
})
|
||||
it('Should throw on additional property 1', () => {
|
||||
Assert.Throws(() => Encoder.Decode(T4, undefined))
|
||||
})
|
||||
it('Should throw on additional property 2', () => {
|
||||
Assert.Throws(() => Encoder.Decode(T4, { x: 1, y: true }))
|
||||
})
|
||||
// ------------------------------------------------------------
|
||||
// Map
|
||||
// ------------------------------------------------------------
|
||||
const T5 = Type.Transform(Type.Module({ A: Type.Object({ x: Type.String(), y: Type.String() })}).Import('A'))
|
||||
.Decode((value) => new Map(Object.entries(value)))
|
||||
.Encode((value) => Object.fromEntries(value.entries()) as any)
|
||||
it('should decode map', () => {
|
||||
const R = Encoder.Decode(T5, { x: 'hello', y: 'world' })
|
||||
Assert.IsInstanceOf(R, Map)
|
||||
Assert.IsEqual(R.get('x'), 'hello')
|
||||
Assert.IsEqual(R.get('y'), 'world')
|
||||
})
|
||||
it('should encode map', () => {
|
||||
const R = Encoder.Encode(
|
||||
T5,
|
||||
new Map([
|
||||
['x', 'hello'],
|
||||
['y', 'world'],
|
||||
]),
|
||||
)
|
||||
Assert.IsEqual(R, { x: 'hello', y: 'world' })
|
||||
})
|
||||
it('Should throw on map decode', () => {
|
||||
Assert.Throws(() => Encoder.Decode(T5, {}))
|
||||
})
|
||||
})
|
||||
@@ -8,6 +8,7 @@ import './constructor'
|
||||
import './date'
|
||||
import './enum'
|
||||
import './function'
|
||||
import './import'
|
||||
import './integer'
|
||||
import './intersect'
|
||||
import './iterator'
|
||||
|
||||
@@ -8,7 +8,7 @@ describe('value/transform/Ref', () => {
|
||||
// Identity
|
||||
// --------------------------------------------------------
|
||||
const N0 = Type.Number({ $id: 'N0' })
|
||||
const T0 = Type.Transform(Type.Ref(N0))
|
||||
const T0 = Type.Transform(Type.Ref('N0'))
|
||||
.Decode((value) => value)
|
||||
.Encode((value) => value)
|
||||
it('Should decode mapped', () => {
|
||||
@@ -26,7 +26,7 @@ describe('value/transform/Ref', () => {
|
||||
// Mapped
|
||||
// --------------------------------------------------------
|
||||
const N1 = Type.Number({ $id: 'N1' })
|
||||
const T1 = Type.Transform(Type.Ref(N1))
|
||||
const T1 = Type.Transform(Type.Unsafe<number>(Type.Ref('N1')))
|
||||
.Decode((value) => value + 1)
|
||||
.Encode((value) => value - 1)
|
||||
it('Should decode mapped', () => {
|
||||
@@ -46,7 +46,7 @@ describe('value/transform/Ref', () => {
|
||||
const N2 = Type.Transform(Type.Number({ $id: 'N2' }))
|
||||
.Decode((value) => value + 1)
|
||||
.Encode((value) => value - 1)
|
||||
const T2 = Type.Transform(Type.Ref(N2))
|
||||
const T2 = Type.Transform(Type.Unsafe<number>(Type.Ref('N2')))
|
||||
.Decode((value) => value + 1)
|
||||
.Encode((value) => value - 1)
|
||||
it('Should decode mapped remote', () => {
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
import { Expect } from './assert'
|
||||
import { Type, TRef, TObject, TNumber } from '@sinclair/typebox'
|
||||
|
||||
// prettier-ignore
|
||||
const Vector: TObject<{
|
||||
x: TNumber;
|
||||
y: TNumber;
|
||||
}> = Type.Object({
|
||||
x: Type.Number(),
|
||||
y: Type.Number(),
|
||||
}, { $id: 'Vector' })
|
||||
|
||||
// prettier-ignore
|
||||
const VectorRef: TRef<TObject<{
|
||||
x: TNumber;
|
||||
y: TNumber;
|
||||
}>> = Type.Ref(Vector)
|
||||
|
||||
// prettier-ignore
|
||||
const Vertex: TObject<{
|
||||
position: TRef<TObject<{
|
||||
x: TNumber;
|
||||
y: TNumber;
|
||||
}>>;
|
||||
texcoord: TRef<TObject<{
|
||||
x: TNumber;
|
||||
y: TNumber;
|
||||
}>>;
|
||||
}> = 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;
|
||||
};
|
||||
}>
|
||||
@@ -3,11 +3,11 @@ import { Type, Static } from '@sinclair/typebox'
|
||||
|
||||
{
|
||||
const T = Type.String({ $id: 'T' })
|
||||
const R = Type.Ref(T)
|
||||
const R = Type.Ref('T')
|
||||
|
||||
type T = Static<typeof T>
|
||||
type R = Static<typeof R>
|
||||
|
||||
Expect(T).ToStatic<string>()
|
||||
Expect(R).ToStatic<string>()
|
||||
Expect(R).ToStatic<unknown>()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user