Skip to content

Commit 92851ea

Browse files
authored
Revision 0.33.4 (#953)
* Add Assert and Parse Functions * Documentation * Version * Benchmarks
1 parent a5b03c0 commit 92851ea

File tree

29 files changed

+592
-236
lines changed

29 files changed

+592
-236
lines changed

Diff for: package-lock.json

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@sinclair/typebox",
3-
"version": "0.33.3",
3+
"version": "0.33.4",
44
"description": "Json Schema Type Builder with Static Type Resolution for TypeScript",
55
"keywords": [
66
"typescript",

Diff for: readme.md

+114-130
Large diffs are not rendered by default.

Diff for: src/compiler/compiler.ts

+5-4
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ import type { TSymbol } from '../type/symbol/index'
7171
import type { TUndefined } from '../type/undefined/index'
7272
import type { TUint8Array } from '../type/uint8array/index'
7373
import type { TVoid } from '../type/void/index'
74+
7475
// ------------------------------------------------------------------
7576
// ValueGuard
7677
// ------------------------------------------------------------------
@@ -104,15 +105,15 @@ export class TypeCheck<T extends TSchema> {
104105
return this.checkFunc(value)
105106
}
106107
/** Decodes a value or throws if error */
107-
public Decode(value: unknown): StaticDecode<T> {
108+
public Decode<R = StaticDecode<T>>(value: unknown): R {
108109
if (!this.checkFunc(value)) throw new TransformDecodeCheckError(this.schema, value, this.Errors(value).First()!)
109-
return this.hasTransform ? TransformDecode(this.schema, this.references, value) : value
110+
return (this.hasTransform ? TransformDecode(this.schema, this.references, value) : value) as never
110111
}
111112
/** Encodes a value or throws if error */
112-
public Encode(value: unknown): StaticEncode<T> {
113+
public Encode<R = StaticEncode<T>>(value: unknown): R {
113114
const encoded = this.hasTransform ? TransformEncode(this.schema, this.references, value) : value
114115
if (!this.checkFunc(encoded)) throw new TransformEncodeCheckError(this.schema, value, this.Errors(value).First()!)
115-
return encoded
116+
return encoded as never
116117
}
117118
}
118119
// ------------------------------------------------------------------

Diff for: src/value/assert/assert.ts

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*--------------------------------------------------------------------------
2+
3+
@sinclair/typebox/value
4+
5+
The MIT License (MIT)
6+
7+
Copyright (c) 2017-2024 Haydn Paterson (sinclair) <[email protected]>
8+
9+
Permission is hereby granted, free of charge, to any person obtaining a copy
10+
of this software and associated documentation files (the "Software"), to deal
11+
in the Software without restriction, including without limitation the rights
12+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13+
copies of the Software, and to permit persons to whom the Software is
14+
furnished to do so, subject to the following conditions:
15+
16+
The above copyright notice and this permission notice shall be included in
17+
all copies or substantial portions of the Software.
18+
19+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25+
THE SOFTWARE.
26+
27+
---------------------------------------------------------------------------*/
28+
29+
import { Errors, ValueErrorIterator, ValueError } from '../../errors/index'
30+
import { TypeBoxError } from '../../type/error/error'
31+
import { TSchema } from '../../type/schema/index'
32+
import { Static } from '../../type/static/index'
33+
import { Check } from '../check/check'
34+
35+
// ------------------------------------------------------------------
36+
// AssertError
37+
// ------------------------------------------------------------------
38+
export class AssertError extends TypeBoxError {
39+
readonly #iterator: ValueErrorIterator
40+
error: ValueError | undefined
41+
constructor(iterator: ValueErrorIterator) {
42+
const error = iterator.First()
43+
super(error === undefined ? 'Invalid Value' : error.message)
44+
this.#iterator = iterator
45+
this.error = error
46+
}
47+
/** Returns an iterator for each error in this value. */
48+
public Errors(): ValueErrorIterator {
49+
return new ValueErrorIterator(this.#Iterator())
50+
}
51+
*#Iterator(): IterableIterator<ValueError> {
52+
if (this.error) yield this.error
53+
yield* this.#iterator
54+
}
55+
}
56+
// ------------------------------------------------------------------
57+
// AssertValue
58+
// ------------------------------------------------------------------
59+
function AssertValue(schema: TSchema, references: TSchema[], value: unknown): unknown {
60+
if (Check(schema, references, value)) return
61+
throw new AssertError(Errors(schema, references, value))
62+
}
63+
// ------------------------------------------------------------------
64+
// Assert
65+
// ------------------------------------------------------------------
66+
/** Asserts a value matches the given type or throws an `AssertError` if invalid */
67+
export function Assert<T extends TSchema>(schema: T, references: TSchema[], value: unknown): asserts value is Static<T>
68+
/** Asserts a value matches the given type or throws an `AssertError` if invalid */
69+
export function Assert<T extends TSchema>(schema: T, value: unknown): asserts value is Static<T>
70+
/** Asserts a value matches the given type or throws an `AssertError` if invalid */
71+
export function Assert(...args: any[]): unknown {
72+
return args.length === 3 ? AssertValue(args[0], args[1], args[2]) : AssertValue(args[0], [], args[1])
73+
}

Diff for: src/value/assert/index.ts

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/*--------------------------------------------------------------------------
2+
3+
@sinclair/typebox/value
4+
5+
The MIT License (MIT)
6+
7+
Copyright (c) 2017-2024 Haydn Paterson (sinclair) <[email protected]>
8+
9+
Permission is hereby granted, free of charge, to any person obtaining a copy
10+
of this software and associated documentation files (the "Software"), to deal
11+
in the Software without restriction, including without limitation the rights
12+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13+
copies of the Software, and to permit persons to whom the Software is
14+
furnished to do so, subject to the following conditions:
15+
16+
The above copyright notice and this permission notice shall be included in
17+
all copies or substantial portions of the Software.
18+
19+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25+
THE SOFTWARE.
26+
27+
---------------------------------------------------------------------------*/
28+
29+
export * from './assert'

Diff for: src/value/cast/cast.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ THE SOFTWARE.
2626
2727
---------------------------------------------------------------------------*/
2828

29-
import { IsStandardObject, IsArray, IsString, IsNumber, IsNull } from '../guard/index'
29+
import { IsObject, IsArray, IsString, IsNumber, IsNull } from '../guard/index'
3030
import { TypeBoxError } from '../../type/error/index'
3131
import { Kind } from '../../type/symbols/index'
3232
import { Create } from '../create/index'
@@ -135,7 +135,7 @@ function FromConstructor(schema: TConstructor, references: TSchema[], value: any
135135
}
136136
function FromIntersect(schema: TIntersect, references: TSchema[], value: any): any {
137137
const created = Create(schema, references)
138-
const mapped = IsStandardObject(created) && IsStandardObject(value) ? { ...(created as any), ...value } : value
138+
const mapped = IsObject(created) && IsObject(value) ? { ...(created as any), ...value } : value
139139
return Check(schema, references, mapped) ? mapped : Create(schema, references)
140140
}
141141
function FromNever(schema: TNever, references: TSchema[], value: any): any {

Diff for: src/value/clean/clean.ts

+8-7
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ import type { TUnion } from '../../type/union/index'
4747
// ------------------------------------------------------------------
4848
// prettier-ignore
4949
import {
50+
HasPropertyKey,
5051
IsString,
5152
IsObject,
5253
IsArray,
@@ -57,13 +58,13 @@ import {
5758
// ------------------------------------------------------------------
5859
// prettier-ignore
5960
import {
60-
IsSchema
61-
} from '../../type/guard/type'
61+
IsKind
62+
} from '../../type/guard/kind'
6263
// ------------------------------------------------------------------
6364
// IsCheckable
6465
// ------------------------------------------------------------------
6566
function IsCheckable(schema: unknown): boolean {
66-
return IsSchema(schema) && schema[Kind] !== 'Unsafe'
67+
return IsKind(schema) && schema[Kind] !== 'Unsafe'
6768
}
6869
// ------------------------------------------------------------------
6970
// Types
@@ -76,7 +77,7 @@ function FromIntersect(schema: TIntersect, references: TSchema[], value: unknown
7677
const unevaluatedProperties = schema.unevaluatedProperties as TSchema
7778
const intersections = schema.allOf.map((schema) => Visit(schema, references, Clone(value)))
7879
const composite = intersections.reduce((acc: any, value: any) => (IsObject(value) ? { ...acc, ...value } : value), {})
79-
if (!IsObject(value) || !IsObject(composite) || !IsSchema(unevaluatedProperties)) return composite
80+
if (!IsObject(value) || !IsObject(composite) || !IsKind(unevaluatedProperties)) return composite
8081
const knownkeys = KeyOfPropertyKeys(schema) as string[]
8182
for (const key of Object.getOwnPropertyNames(value)) {
8283
if (knownkeys.includes(key)) continue
@@ -90,11 +91,11 @@ function FromObject(schema: TObject, references: TSchema[], value: unknown): any
9091
if (!IsObject(value) || IsArray(value)) return value // Check IsArray for AllowArrayObject configuration
9192
const additionalProperties = schema.additionalProperties as TSchema
9293
for (const key of Object.getOwnPropertyNames(value)) {
93-
if (key in schema.properties) {
94+
if (HasPropertyKey(schema.properties, key)) {
9495
value[key] = Visit(schema.properties[key], references, value[key])
9596
continue
9697
}
97-
if (IsSchema(additionalProperties) && Check(additionalProperties, references, value[key])) {
98+
if (IsKind(additionalProperties) && Check(additionalProperties, references, value[key])) {
9899
value[key] = Visit(additionalProperties, references, value[key])
99100
continue
100101
}
@@ -113,7 +114,7 @@ function FromRecord(schema: TRecord, references: TSchema[], value: unknown): any
113114
value[key] = Visit(propertySchema, references, value[key])
114115
continue
115116
}
116-
if (IsSchema(additionalProperties) && Check(additionalProperties, references, value[key])) {
117+
if (IsKind(additionalProperties) && Check(additionalProperties, references, value[key])) {
117118
value[key] = Visit(additionalProperties, references, value[key])
118119
continue
119120
}

Diff for: src/value/clone/clone.ts

+20-12
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,16 @@ THE SOFTWARE.
2626
2727
---------------------------------------------------------------------------*/
2828

29-
import type { ObjectType, ArrayType, TypedArrayType, ValueType } from '../guard/index'
29+
import type { ObjectType as FromObject, ArrayType as FromArray, TypedArrayType, ValueType } from '../guard/index'
3030

3131
// ------------------------------------------------------------------
3232
// ValueGuard
3333
// ------------------------------------------------------------------
34-
import { IsArray, IsDate, IsStandardObject, IsTypedArray, IsValueType } from '../guard/index'
34+
import { IsArray, IsDate, IsMap, IsSet, IsObject, IsTypedArray, IsValueType } from '../guard/index'
3535
// ------------------------------------------------------------------
3636
// Clonable
3737
// ------------------------------------------------------------------
38-
function ObjectType(value: ObjectType): any {
38+
function FromObject(value: FromObject): any {
3939
const Acc = {} as Record<PropertyKey, unknown>
4040
for (const key of Object.getOwnPropertyNames(value)) {
4141
Acc[key] = Clone(value[key])
@@ -45,27 +45,35 @@ function ObjectType(value: ObjectType): any {
4545
}
4646
return Acc
4747
}
48-
function ArrayType(value: ArrayType): any {
48+
function FromArray(value: FromArray): any {
4949
return value.map((element: any) => Clone(element))
5050
}
51-
function TypedArrayType(value: TypedArrayType): any {
51+
function FromTypedArray(value: TypedArrayType): any {
5252
return value.slice()
5353
}
54-
function DateType(value: Date): any {
54+
function FromMap(value: Map<unknown, unknown>): any {
55+
return new Map(Clone([...value.entries()]))
56+
}
57+
function FromSet(value: Set<unknown>): any {
58+
return new Set(Clone([...value.entries()]))
59+
}
60+
function FromDate(value: Date): any {
5561
return new Date(value.toISOString())
5662
}
57-
function ValueType(value: ValueType): any {
63+
function FromValue(value: ValueType): any {
5864
return value
5965
}
6066
// ------------------------------------------------------------------
6167
// Clone
6268
// ------------------------------------------------------------------
6369
/** Returns a clone of the given value */
6470
export function Clone<T extends unknown>(value: T): T {
65-
if (IsArray(value)) return ArrayType(value)
66-
if (IsDate(value)) return DateType(value)
67-
if (IsStandardObject(value)) return ObjectType(value)
68-
if (IsTypedArray(value)) return TypedArrayType(value)
69-
if (IsValueType(value)) return ValueType(value)
71+
if (IsArray(value)) return FromArray(value)
72+
if (IsDate(value)) return FromDate(value)
73+
if (IsTypedArray(value)) return FromTypedArray(value)
74+
if (IsMap(value)) return FromMap(value)
75+
if (IsSet(value)) return FromSet(value)
76+
if (IsObject(value)) return FromObject(value)
77+
if (IsValueType(value)) return FromValue(value)
7078
throw new Error('ValueClone: Unable to clone value')
7179
}

Diff for: src/value/convert/convert.ts

+19-22
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ function TryConvertLiteral(schema: TLiteral, value: unknown) {
106106
IsString(schema.const) ? TryConvertLiteralString(value, schema.const) :
107107
IsNumber(schema.const) ? TryConvertLiteralNumber(value, schema.const) :
108108
IsBoolean(schema.const) ? TryConvertLiteralBoolean(value, schema.const) :
109-
Clone(value)
109+
value
110110
)
111111
}
112112
function TryConvertBoolean(value: unknown) {
@@ -156,7 +156,7 @@ function TryConvertDate(value: unknown) {
156156
// ------------------------------------------------------------------
157157
// Default
158158
// ------------------------------------------------------------------
159-
function Default(value: any) {
159+
function Default(value: unknown): unknown {
160160
return value
161161
}
162162
// ------------------------------------------------------------------
@@ -192,26 +192,21 @@ function FromNumber(schema: TNumber, references: TSchema[], value: any): unknown
192192
}
193193
// prettier-ignore
194194
function FromObject(schema: TObject, references: TSchema[], value: any): unknown {
195-
const isConvertable = IsObject(value)
196-
if(!isConvertable) return value
197-
const result: Record<PropertyKey, unknown> = {}
198-
for(const key of Object.keys(value)) {
199-
result[key] = HasPropertyKey(schema.properties, key)
200-
? Visit(schema.properties[key], references, value[key])
201-
: value[key]
195+
if(!IsObject(value)) return value
196+
for(const key of Object.getOwnPropertyNames(schema.properties)) {
197+
value[key] = Visit(schema.properties[key], references, value[key])
202198
}
203-
return result
199+
return value
204200
}
205201
function FromRecord(schema: TRecord, references: TSchema[], value: any): unknown {
206202
const isConvertable = IsObject(value)
207203
if (!isConvertable) return value
208204
const propertyKey = Object.getOwnPropertyNames(schema.patternProperties)[0]
209205
const property = schema.patternProperties[propertyKey]
210-
const result = {} as Record<string, unknown>
211206
for (const [propKey, propValue] of Object.entries(value)) {
212-
result[propKey] = Visit(property, references, propValue)
207+
value[propKey] = Visit(property, references, propValue)
213208
}
214-
return result
209+
return value
215210
}
216211
function FromRef(schema: TRef, references: TSchema[], value: any): unknown {
217212
return Visit(Deref(schema, references), references, value)
@@ -233,21 +228,25 @@ function FromTuple(schema: TTuple, references: TSchema[], value: any): unknown {
233228
return (index < schema.items!.length)
234229
? Visit(schema.items![index], references, value)
235230
: value
236-
})
231+
})
237232
}
238233
function FromUndefined(schema: TUndefined, references: TSchema[], value: any): unknown {
239234
return TryConvertUndefined(value)
240235
}
241236
function FromUnion(schema: TUnion, references: TSchema[], value: any): unknown {
242237
for (const subschema of schema.anyOf) {
243-
const converted = Visit(subschema, references, value)
238+
const converted = Visit(subschema, references, Clone(value))
244239
if (!Check(subschema, references, converted)) continue
245240
return converted
246241
}
247242
return value
248243
}
244+
function AddReference(references: TSchema[], schema: TSchema): TSchema[] {
245+
references.push(schema)
246+
return references
247+
}
249248
function Visit(schema: TSchema, references: TSchema[], value: any): unknown {
250-
const references_ = IsString(schema.$id) ? [...references, schema] : references
249+
const references_ = IsString(schema.$id) ? AddReference(references, schema) : references
251250
const schema_ = schema as any
252251
switch (schema[Kind]) {
253252
case 'Array':
@@ -293,14 +292,12 @@ function Visit(schema: TSchema, references: TSchema[], value: any): unknown {
293292
// ------------------------------------------------------------------
294293
// Convert
295294
// ------------------------------------------------------------------
296-
/** Converts any type mismatched values to their target type if a reasonable conversion is possible. */
295+
/** `[Mutable]` Converts any type mismatched values to their target type if a reasonable conversion is possible. */
297296
export function Convert(schema: TSchema, references: TSchema[], value: unknown): unknown
298-
/** Converts any type mismatched values to their target type if a reasonable conversion is possible. */
297+
/** `[Mutable]` Converts any type mismatched values to their target type if a reasonable conversion is possible. */
299298
export function Convert(schema: TSchema, value: unknown): unknown
300-
/** Converts any type mismatched values to their target type if a reasonable conversion is possible. */
299+
/** `[Mutable]` Converts any type mismatched values to their target type if a reasonable conversion is possible. */
301300
// prettier-ignore
302301
export function Convert(...args: any[]) {
303-
return args.length === 3
304-
? Visit(args[0], args[1], args[2])
305-
: Visit(args[0], [], args[1])
302+
return args.length === 3 ? Visit(args[0], args[1], args[2]) : Visit(args[0], [], args[1])
306303
}

0 commit comments

Comments
 (0)