Skip to content

Commit

Permalink
Revision 0.32.0
Browse files Browse the repository at this point in the history
  • Loading branch information
sinclairzx81 committed Nov 29, 2023
1 parent 9094944 commit 55a4bba
Show file tree
Hide file tree
Showing 15 changed files with 161 additions and 166 deletions.
3 changes: 0 additions & 3 deletions benchmark/compression/module/typebox-type-array.ts

This file was deleted.

3 changes: 0 additions & 3 deletions benchmark/compression/module/typebox-type-boolean.ts

This file was deleted.

3 changes: 0 additions & 3 deletions benchmark/compression/module/typebox-type-extends.ts

This file was deleted.

3 changes: 0 additions & 3 deletions benchmark/compression/module/typebox-type-null.ts

This file was deleted.

3 changes: 0 additions & 3 deletions benchmark/compression/module/typebox-type-number.ts

This file was deleted.

3 changes: 0 additions & 3 deletions benchmark/compression/module/typebox-type-object.ts

This file was deleted.

3 changes: 0 additions & 3 deletions benchmark/compression/module/typebox-type-record.ts

This file was deleted.

3 changes: 0 additions & 3 deletions benchmark/compression/module/typebox-type-string.ts

This file was deleted.

3 changes: 0 additions & 3 deletions benchmark/compression/module/typebox-value-cast.ts

This file was deleted.

3 changes: 0 additions & 3 deletions benchmark/compression/module/typebox-value-check.ts

This file was deleted.

3 changes: 0 additions & 3 deletions benchmark/compression/module/typebox-value-convert.ts

This file was deleted.

3 changes: 0 additions & 3 deletions benchmark/compression/module/typebox-value-create.ts

This file was deleted.

2 changes: 1 addition & 1 deletion examples/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,4 @@ const D = Type.Transform(Type.Object({ date: A }))
})
.Encode((o) => o)

const R1 = Value.Decode(C, { date: new Date().toISOString() })
const R1 = Value.Decode(C, [], { date: new Date().toISOString() })
142 changes: 68 additions & 74 deletions src/value/cast/cast.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,53 +26,66 @@ THE SOFTWARE.
---------------------------------------------------------------------------*/

import { IsPlainObject, IsArray, IsString, IsNumber, IsNull } from '../guard/guard'
import { TypeRegistry } from '../../type/registry/index'
import * as Types from '../../type/index'
import { Create } from '../create/create'
import { Check } from '../check/check'
import { Clone } from '../clone/clone'
import { Deref } from '../deref/deref'
import { IsPlainObject, IsArray, IsString, IsNumber, IsNull } from '../guard/index'
import { Kind } from '../../type/symbols/index'
import { Create } from '../create/index'
import { Check } from '../check/index'
import { Clone } from '../clone/index'
import { Deref } from '../deref/index'

// --------------------------------------------------------------------------
import type { TSchema } from '../../type/schema/index'
import type { Static } from '../../type/static/index'
import type { TArray } from '../../type/array/index'
import type { TConstructor } from '../../type/constructor/index'
import type { TIntersect } from '../../type/intersect/index'
import type { TObject } from '../../type/object/index'
import type { TRecord } from '../../type/record/index'
import type { TRef } from '../../type/ref/index'
import type { TThis } from '../../type/recursive/index'
import type { TTuple } from '../../type/tuple/index'
import type { TUnion } from '../../type/union/index'
import type { TNever } from '../../type/never/index'

// ------------------------------------------------------------------
// Errors
// --------------------------------------------------------------------------
// ------------------------------------------------------------------
export class ValueCastArrayUniqueItemsTypeError extends Error {
constructor(public readonly schema: Types.TSchema, public readonly value: unknown) {
constructor(public readonly schema: TSchema, public readonly value: unknown) {
super('Array cast produced invalid data due to uniqueItems constraint')
}
}
export class ValueCastNeverTypeError extends Error {
constructor(public readonly schema: Types.TSchema) {
constructor(public readonly schema: TSchema) {
super('Never types cannot be cast')
}
}
export class ValueCastRecursiveTypeError extends Error {
constructor(public readonly schema: Types.TSchema) {
constructor(public readonly schema: TSchema) {
super('Cannot cast recursive schemas')
}
}
export class ValueCastUnknownTypeError extends Error {
constructor(public readonly schema: Types.TSchema) {
constructor(public readonly schema: TSchema) {
super('Unknown type')
}
}
// --------------------------------------------------------------------------
// The following will score a schema against a value. For objects, the score
// is the tally of points awarded for each property of the value. Property
// points are (1.0 / propertyCount) to prevent large property counts biasing
// results. Properties that match literal values are maximally awarded as
// literals are typically used as union discriminator fields.
// --------------------------------------------------------------------------
// ------------------------------------------------------------------
// The following will score a schema against a value. For objects,
// the score is the tally of points awarded for each property of
// the value. Property points are (1.0 / propertyCount) to prevent
// large property counts biasing results. Properties that match
// literal values are maximally awarded as literals are typically
// used as union discriminator fields.
// ------------------------------------------------------------------
namespace UnionCastCreate {
function Score(schema: Types.TSchema, references: Types.TSchema[], value: any): number {
if (schema[Types.Kind] === 'Object' && typeof value === 'object' && !IsNull(value)) {
const object = schema as Types.TObject
function Score(schema: TSchema, references: TSchema[], value: any): number {
if (schema[Kind] === 'Object' && typeof value === 'object' && !IsNull(value)) {
const object = schema as TObject
const keys = Object.getOwnPropertyNames(value)
const entries = Object.entries(object.properties)
const [point, max] = [1 / entries.length, entries.length]
return entries.reduce((acc, [key, schema]) => {
const literal = schema[Types.Kind] === 'Literal' && schema.const === value[key] ? max : 0
const literal = schema[Kind] === 'Literal' && schema.const === value[key] ? max : 0
const checks = Check(schema, references, value[key]) ? point : 0
const exists = keys.includes(key) ? point : 0
return acc + (literal + checks + exists)
Expand All @@ -81,7 +94,7 @@ namespace UnionCastCreate {
return Check(schema, references, value) ? 1 : 0
}
}
function Select(union: Types.TUnion, references: Types.TSchema[], value: any): Types.TSchema {
function Select(union: TUnion, references: TSchema[], value: any): TSchema {
let [select, best] = [union.anyOf[0], 0]
for (const schema of union.anyOf) {
const score = Score(schema, references, value)
Expand All @@ -92,7 +105,7 @@ namespace UnionCastCreate {
}
return select
}
export function Create(union: Types.TUnion, references: Types.TSchema[], value: any) {
export function Create(union: TUnion, references: TSchema[], value: any) {
if ('default' in union) {
return union.default
} else {
Expand All @@ -101,19 +114,19 @@ namespace UnionCastCreate {
}
}
}
// --------------------------------------------------------------------------
// ------------------------------------------------------------------
// Default
// --------------------------------------------------------------------------
export function DefaultClone(schema: Types.TSchema, references: Types.TSchema[], value: any): any {
// ------------------------------------------------------------------
export function DefaultClone(schema: TSchema, references: TSchema[], value: any): any {
return Check(schema, references, value) ? Clone(value) : Create(schema, references)
}
export function Default(schema: Types.TSchema, references: Types.TSchema[], value: any): any {
export function Default(schema: TSchema, references: TSchema[], value: any): any {
return Check(schema, references, value) ? value : Create(schema, references)
}
// --------------------------------------------------------------------------
// ------------------------------------------------------------------
// Cast
// --------------------------------------------------------------------------
function TArray(schema: Types.TArray, references: Types.TSchema[], value: any): any {
// ------------------------------------------------------------------
function TArray(schema: TArray, references: TSchema[], value: any): any {
if (Check(schema, references, value)) return Clone(value)
const created = IsArray(value) ? Clone(value) : Create(schema, references)
const minimum = IsNumber(schema.minItems) && created.length < schema.minItems ? [...created, ...Array.from({ length: schema.minItems - created.length }, () => null)] : created
Expand All @@ -124,25 +137,25 @@ function TArray(schema: Types.TArray, references: Types.TSchema[], value: any):
if (!Check(schema, references, unique)) throw new ValueCastArrayUniqueItemsTypeError(schema, unique)
return unique
}
function TConstructor(schema: Types.TConstructor, references: Types.TSchema[], value: any): any {
function TConstructor(schema: TConstructor, references: TSchema[], value: any): any {
if (Check(schema, references, value)) return Create(schema, references)
const required = new Set(schema.returns.required || [])
const result = function () {}
for (const [key, property] of Object.entries(schema.returns.properties)) {
if (!required.has(key) && value.prototype[key] === undefined) continue
result.prototype[key] = Visit(property as Types.TSchema, references, value.prototype[key])
result.prototype[key] = Visit(property as TSchema, references, value.prototype[key])
}
return result
}
function TIntersect(schema: Types.TIntersect, references: Types.TSchema[], value: any): any {
function TIntersect(schema: TIntersect, references: TSchema[], value: any): any {
const created = Create(schema, references)
const mapped = IsPlainObject(created) && IsPlainObject(value) ? { ...(created as any), ...value } : value
return Check(schema, references, mapped) ? mapped : Create(schema, references)
}
function TNever(schema: Types.TNever, references: Types.TSchema[], value: any): any {
function TNever(schema: TNever, references: TSchema[], value: any): any {
throw new ValueCastNeverTypeError(schema)
}
function TObject(schema: Types.TObject, references: Types.TSchema[], value: any): any {
function TObject(schema: TObject, references: TSchema[], value: any): any {
if (Check(schema, references, value)) return value
if (value === null || typeof value !== 'object') return Create(schema, references)
const required = new Set(schema.required || [])
Expand All @@ -161,7 +174,7 @@ function TObject(schema: Types.TObject, references: Types.TSchema[], value: any)
}
return result
}
function TRecord(schema: Types.TRecord<any, any>, references: Types.TSchema[], value: any): any {
function TRecord(schema: TRecord<any, any>, references: TSchema[], value: any): any {
if (Check(schema, references, value)) return Clone(value)
if (value === null || typeof value !== 'object' || Array.isArray(value) || value instanceof Date) return Create(schema, references)
const subschemaPropertyName = Object.getOwnPropertyNames(schema.patternProperties)[0]
Expand All @@ -172,28 +185,28 @@ function TRecord(schema: Types.TRecord<any, any>, references: Types.TSchema[], v
}
return result
}
function TRef(schema: Types.TRef<any>, references: Types.TSchema[], value: any): any {
function TRef(schema: TRef<any>, references: TSchema[], value: any): any {
return Visit(Deref(schema, references), references, value)
}
function TThis(schema: Types.TThis, references: Types.TSchema[], value: any): any {
function TThis(schema: TThis, references: TSchema[], value: any): any {
return Visit(Deref(schema, references), references, value)
}
function TTuple(schema: Types.TTuple<any[]>, references: Types.TSchema[], value: any): any {
function TTuple(schema: TTuple<any[]>, references: TSchema[], value: any): any {
if (Check(schema, references, value)) return Clone(value)
if (!IsArray(value)) return Create(schema, references)
if (schema.items === undefined) return []
return schema.items.map((schema, index) => Visit(schema, references, value[index]))
}
function TUnion(schema: Types.TUnion, references: Types.TSchema[], value: any): any {
function TUnion(schema: TUnion, references: TSchema[], value: any): any {
return Check(schema, references, value) ? Clone(value) : UnionCastCreate.Create(schema, references, value)
}
function Visit(schema: Types.TSchema, references: Types.TSchema[], value: any): any {
function Visit(schema: TSchema, references: TSchema[], value: any): any {
const references_ = IsString(schema.$id) ? [...references, schema] : references
const schema_ = schema as any
switch (schema[Types.Kind]) {
// ------------------------------------------------------
switch (schema[Kind]) {
// --------------------------------------------------------------
// Structural
// ------------------------------------------------------
// --------------------------------------------------------------
case 'Array':
return TArray(schema_, references_, value)
case 'Constructor':
Expand All @@ -214,46 +227,27 @@ function Visit(schema: Types.TSchema, references: Types.TSchema[], value: any):
return TTuple(schema_, references_, value)
case 'Union':
return TUnion(schema_, references_, value)
// ------------------------------------------------------
// --------------------------------------------------------------
// DefaultClone
// ------------------------------------------------------
// --------------------------------------------------------------
case 'Date':
case 'Symbol':
case 'Uint8Array':
return DefaultClone(schema, references, value)
// ------------------------------------------------------
// --------------------------------------------------------------
// Default
// ------------------------------------------------------
case 'Any':
case 'AsyncIterator':
case 'BigInt':
case 'Boolean':
case 'Function':
case 'Integer':
case 'Iterator':
case 'Literal':
case 'Not':
case 'Null':
case 'Number':
case 'Promise':
case 'String':
case 'TemplateLiteral':
case 'Undefined':
case 'Unknown':
case 'Void':
return Default(schema_, references_, value)
// --------------------------------------------------------------
default:
if (!TypeRegistry.Has(schema_[Types.Kind])) throw new ValueCastUnknownTypeError(schema_)
return Default(schema_, references_, value)
}
}
// --------------------------------------------------------------------------
// ------------------------------------------------------------------
// Cast
// --------------------------------------------------------------------------
// ------------------------------------------------------------------
/** Casts a value into a given type and references. The return value will retain as much information of the original value as possible. */
export function Cast<T extends Types.TSchema>(schema: T, references: Types.TSchema[], value: unknown): Types.Static<T>
export function Cast<T extends TSchema>(schema: T, references: TSchema[], value: unknown): Static<T>
/** Casts a value into a given type. The return value will retain as much information of the original value as possible. */
export function Cast<T extends Types.TSchema>(schema: T, value: unknown): Types.Static<T>
export function Cast<T extends TSchema>(schema: T, value: unknown): Static<T>
/** Casts a value into a given type. The return value will retain as much information of the original value as possible. */
export function Cast(...args: any[]) {
return args.length === 3 ? Visit(args[0], args[1], args[2]) : Visit(args[0], [], args[1])
Expand Down
Loading

0 comments on commit 55a4bba

Please sign in to comment.