Skip to content

Commit 333c2a1

Browse files
authored
Revision 0.33.0 (#941)
* Implement Immutable Types * Update TypeScript * Support Clone and Freeze Instancing * Ensure Options Cloned on Mapped Key and Result * Revision 0.33.0
1 parent e686997 commit 333c2a1

File tree

89 files changed

+521
-410
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

89 files changed

+521
-410
lines changed

hammer.mjs

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export async function benchmark() {
3232
// Test
3333
// -------------------------------------------------------------------------------
3434
export async function test_typescript() {
35-
for (const version of ['4.9.5', '5.0.4', '5.1.3', '5.1.6', '5.2.2', '5.3.2', '5.3.3', '5.4.3', '5.4.5', '5.5.2', 'next', 'latest']) {
35+
for (const version of ['4.9.5', '5.0.4', '5.1.3', '5.1.6', '5.2.2', '5.3.2', '5.3.3', '5.4.3', '5.4.5', '5.5.2', '5.5.3', 'next', 'latest']) {
3636
await shell(`npm install typescript@${version} --no-save`)
3737
await test_static()
3838
}

package-lock.json

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

package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@sinclair/typebox",
3-
"version": "0.32.35",
3+
"version": "0.33.0",
44
"description": "Json Schema Type Builder with Static Type Resolution for TypeScript",
55
"keywords": [
66
"typescript",
@@ -37,6 +37,6 @@
3737
"ajv-formats": "^2.1.1",
3838
"mocha": "^10.4.0",
3939
"prettier": "^2.7.1",
40-
"typescript": "^5.5.3"
40+
"typescript": "^5.5.4"
4141
}
4242
}

src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ THE SOFTWARE.
3030
// Infrastructure
3131
// ------------------------------------------------------------------
3232
export * from './type/clone/index'
33+
export * from './type/create/index'
3334
export * from './type/error/index'
3435
export * from './type/guard/index'
3536
export * from './type/helpers/index'

src/system/policy.ts

+13-2
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,20 @@ import { IsObject, IsArray, IsNumber, IsUndefined } from '../value/guard/index'
3030

3131
export namespace TypeSystemPolicy {
3232
// ------------------------------------------------------------------
33-
// TypeSystemPolicy
33+
// TypeSystemPolicy: Instancing
34+
// ------------------------------------------------------------------
35+
/**
36+
* Configures the instantiation behavior of TypeBox types. The `default` option assigns raw JavaScript
37+
* references for embedded types, which may cause side effects if type properties are explicitly updated
38+
* outside the TypeBox type builder. The `clone` option creates copies of any shared types upon creation,
39+
* preventing unintended side effects. The `freeze` option applies `Object.freeze()` to the type, making
40+
* it fully readonly and immutable. Implementations should use `default` whenever possible, as it is the
41+
* fastest way to instantiate types. The default setting is `default`.
42+
*/
43+
export let InstanceMode: 'default' | 'clone' | 'freeze' = 'default'
44+
// ------------------------------------------------------------------
45+
// TypeSystemPolicy: Checking
3446
// ------------------------------------------------------------------
35-
/** Shared assertion routines used by the value and errors modules */
3647
/** Sets whether TypeBox should assert optional properties using the TypeScript `exactOptionalPropertyTypes` assertion policy. The default is `false` */
3748
export let ExactOptionalPropertyTypes: boolean = false
3849
/** Sets whether arrays should be treated as a kind of objects. The default is `false` */

src/type/any/any.ts

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

29+
import { CreateType } from '../create/index'
2930
import type { TSchema, SchemaOptions } from '../schema/index'
3031
import { Kind } from '../symbols/index'
3132

@@ -35,6 +36,6 @@ export interface TAny extends TSchema {
3536
}
3637

3738
/** `[Json]` Creates an Any type */
38-
export function Any(options: SchemaOptions = {}): TAny {
39-
return { ...options, [Kind]: 'Any' } as never
39+
export function Any(options?: SchemaOptions): TAny {
40+
return CreateType({ [Kind]: 'Any' }, options) as never
4041
}

src/type/array/array.ts

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

29-
import { CloneType } from '../clone/type'
29+
import { CreateType } from '../create/type'
3030
import { Ensure } from '../helpers/index'
3131
import type { SchemaOptions, TSchema } from '../schema/index'
3232
import type { Static } from '../static/index'
@@ -54,11 +54,6 @@ export interface TArray<T extends TSchema = TSchema> extends TSchema, ArrayOptio
5454
items: T
5555
}
5656
/** `[Json]` Creates an Array type */
57-
export function Array<T extends TSchema>(schema: T, options: ArrayOptions = {}): TArray<T> {
58-
return {
59-
...options,
60-
[Kind]: 'Array',
61-
type: 'array',
62-
items: CloneType(schema),
63-
} as never
57+
export function Array<T extends TSchema>(items: T, options?: ArrayOptions): TArray<T> {
58+
return CreateType({ [Kind]: 'Array', type: 'array', items }, options) as never
6459
}

src/type/async-iterator/async-iterator.ts

+3-8
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ THE SOFTWARE.
2929
import type { TSchema, SchemaOptions } from '../schema/index'
3030
import type { Static } from '../static/index'
3131
import { Kind } from '../symbols/index'
32-
import { CloneType } from '../clone/type'
32+
import { CreateType } from '../create/type'
3333

3434
export interface TAsyncIterator<T extends TSchema = TSchema> extends TSchema {
3535
[Kind]: 'AsyncIterator'
@@ -38,11 +38,6 @@ export interface TAsyncIterator<T extends TSchema = TSchema> extends TSchema {
3838
items: T
3939
}
4040
/** `[JavaScript]` Creates a AsyncIterator type */
41-
export function AsyncIterator<T extends TSchema>(items: T, options: SchemaOptions = {}): TAsyncIterator<T> {
42-
return {
43-
...options,
44-
[Kind]: 'AsyncIterator',
45-
type: 'AsyncIterator',
46-
items: CloneType(items),
47-
} as never
41+
export function AsyncIterator<T extends TSchema>(items: T, options?: SchemaOptions): TAsyncIterator<T> {
42+
return CreateType({ [Kind]: 'AsyncIterator', type: 'AsyncIterator', items }, options) as never
4843
}

src/type/awaited/awaited.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import type { TSchema, SchemaOptions } from '../schema/index'
3030
import { Intersect, type TIntersect } from '../intersect/index'
3131
import { Union, type TUnion } from '../union/index'
3232
import { type TPromise } from '../promise/index'
33-
import { CloneType } from '../clone/type'
33+
import { CreateType } from '../create/type'
3434

3535
// ------------------------------------------------------------------
3636
// TypeGuard
@@ -96,6 +96,6 @@ export type TAwaited<T extends TSchema> =
9696
T extends TPromise<infer S> ? TAwaited<S> :
9797
T
9898
/** `[JavaScript]` Constructs a type by recursively unwrapping Promise types */
99-
export function Awaited<T extends TSchema>(T: T, options: SchemaOptions = {}): TAwaited<T> {
100-
return CloneType(AwaitedResolve(T), options)
99+
export function Awaited<T extends TSchema>(T: T, options?: SchemaOptions): TAwaited<T> {
100+
return CreateType(AwaitedResolve(T), options) as never
101101
}

src/type/bigint/bigint.ts

+3-6
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ THE SOFTWARE.
2828

2929
import type { TSchema, SchemaOptions } from '../schema/index'
3030
import { Kind } from '../symbols/index'
31+
import { CreateType } from '../create/index'
3132

3233
export interface BigIntOptions extends SchemaOptions {
3334
exclusiveMaximum?: bigint
@@ -42,10 +43,6 @@ export interface TBigInt extends TSchema, BigIntOptions {
4243
type: 'bigint'
4344
}
4445
/** `[JavaScript]` Creates a BigInt type */
45-
export function BigInt(options: BigIntOptions = {}): TBigInt {
46-
return {
47-
...options,
48-
[Kind]: 'BigInt',
49-
type: 'bigint',
50-
} as never
46+
export function BigInt(options?: BigIntOptions): TBigInt {
47+
return CreateType({ [Kind]: 'BigInt', type: 'bigint' }, options) as never
5148
}

src/type/boolean/boolean.ts

+3-6
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,14 @@ THE SOFTWARE.
2828

2929
import type { TSchema, SchemaOptions } from '../schema/index'
3030
import { Kind } from '../symbols/index'
31+
import { CreateType } from '../create/index'
3132

3233
export interface TBoolean extends TSchema {
3334
[Kind]: 'Boolean'
3435
static: boolean
3536
type: 'boolean'
3637
}
3738
/** `[Json]` Creates a Boolean type */
38-
export function Boolean(options: SchemaOptions = {}): TBoolean {
39-
return {
40-
...options,
41-
[Kind]: 'Boolean',
42-
type: 'boolean',
43-
} as never
39+
export function Boolean(options?: SchemaOptions): TBoolean {
40+
return CreateType({ [Kind]: 'Boolean', type: 'boolean' }, options) as never
4441
}

src/type/clone/type.ts

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

29-
import type { TSchema, SchemaOptions } from '../schema/index'
29+
import { TSchema, SchemaOptions } from '../schema/index'
3030
import { Clone } from './value'
3131

3232
/** Clones a Rest */
3333
export function CloneRest<T extends TSchema[]>(schemas: T): T {
3434
return schemas.map((schema) => CloneType(schema)) as never
3535
}
3636
/** Clones a Type */
37-
export function CloneType<T extends TSchema>(schema: T, options: SchemaOptions = {}): T {
38-
return { ...Clone(schema), ...options }
37+
export function CloneType<T extends TSchema>(schema: T, options?: SchemaOptions): T {
38+
return options === undefined ? Clone(schema) : Clone({ ...options, ...schema })
3939
}

src/type/composite/composite.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ type TCompositeEvaluate<
114114
// prettier-ignore
115115
export type TComposite<T extends TSchema[]> = TCompositeEvaluate<T>
116116
// prettier-ignore
117-
export function Composite<T extends TSchema[]>(T: [...T], options: ObjectOptions = {}): TComposite<T> {
117+
export function Composite<T extends TSchema[]>(T: [...T], options?: ObjectOptions): TComposite<T> {
118118
const K = CompositeKeys(T)
119119
const P = CompositeProperties(T, K)
120120
const R = Object(P, options)

src/type/const/const.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ import { Readonly, type TReadonly } from '../readonly/index'
4444
import { Undefined, type TUndefined } from '../undefined/index'
4545
import { Uint8Array, type TUint8Array } from '../uint8array/index'
4646
import { Unknown, type TUnknown } from '../unknown/index'
47-
import { CloneType } from '../clone/index'
47+
import { CreateType } from '../create/index'
4848

4949
// ------------------------------------------------------------------
5050
// ValueGuard
@@ -130,6 +130,6 @@ function FromValue<T, Root extends boolean>(value: T, root: Root): FromValue<T,
130130
export type TConst<T> = FromValue<T, true>
131131

132132
/** `[JavaScript]` Creates a readonly const type from the given value. */
133-
export function Const</* const (not supported in 4.0) */ T>(T: T, options: SchemaOptions = {}): TConst<T> {
134-
return CloneType(FromValue(T, true), options) as never
133+
export function Const</* const (not supported in 4.0) */ T>(T: T, options?: SchemaOptions): TConst<T> {
134+
return CreateType(FromValue(T, true), options) as never
135135
}

src/type/constructor-parameters/constructor-parameters.ts

+2-3
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ import type { TSchema, SchemaOptions } from '../schema/index'
3030
import type { Ensure } from '../helpers/index'
3131
import type { TConstructor } from '../constructor/index'
3232
import { Tuple, type TTuple } from '../tuple/index'
33-
import { CloneRest } from '../clone/type'
3433

3534
// ------------------------------------------------------------------
3635
// ConstructorParameters
@@ -41,6 +40,6 @@ export type TConstructorParameters<T extends TConstructor<TSchema[], TSchema>> =
4140
)
4241

4342
/** `[JavaScript]` Extracts the ConstructorParameters from the given Constructor type */
44-
export function ConstructorParameters<T extends TConstructor<TSchema[], TSchema>>(schema: T, options: SchemaOptions = {}): TConstructorParameters<T> {
45-
return Tuple(CloneRest(schema.parameters), { ...options })
43+
export function ConstructorParameters<T extends TConstructor<TSchema[], TSchema>>(schema: T, options?: SchemaOptions): TConstructorParameters<T> {
44+
return Tuple(schema.parameters, options)
4645
}

src/type/constructor/constructor.ts

+2-8
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import type { Ensure } from '../helpers/index'
3232
import type { TReadonlyOptional } from '../readonly-optional/index'
3333
import type { TReadonly } from '../readonly/index'
3434
import type { TOptional } from '../optional/index'
35-
import { CloneType, CloneRest } from '../clone/type'
35+
import { CreateType } from '../create/type'
3636
import { Kind } from '../symbols/index'
3737

3838
// ------------------------------------------------------------------
@@ -66,11 +66,5 @@ export interface TConstructor<T extends TSchema[] = TSchema[], U extends TSchema
6666
}
6767
/** `[JavaScript]` Creates a Constructor type */
6868
export function Constructor<T extends TSchema[], U extends TSchema>(parameters: [...T], returns: U, options?: SchemaOptions): TConstructor<T, U> {
69-
return {
70-
...options,
71-
[Kind]: 'Constructor',
72-
type: 'Constructor',
73-
parameters: CloneRest(parameters),
74-
returns: CloneType(returns),
75-
} as never
69+
return CreateType({ [Kind]: 'Constructor', type: 'Constructor', parameters, returns }, options) as never
7670
}

src/type/create/immutable.ts

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*--------------------------------------------------------------------------
2+
3+
@sinclair/typebox/type
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 * as ValueGuard from '../guard/value'
30+
31+
function ImmutableArray(value: unknown[]) {
32+
return globalThis.Object.freeze(value as any).map((value: unknown) => Immutable(value as any))
33+
}
34+
function ImmutableDate(value: Date) {
35+
return value
36+
}
37+
function ImmutableUint8Array(value: Uint8Array) {
38+
return value
39+
}
40+
function ImmutableRegExp(value: RegExp) {
41+
return value
42+
}
43+
function ImmutableObject(value: Record<keyof any, unknown>) {
44+
const result = {} as Record<PropertyKey, unknown>
45+
for (const key of Object.getOwnPropertyNames(value)) {
46+
result[key] = Immutable(value[key])
47+
}
48+
for (const key of Object.getOwnPropertySymbols(value)) {
49+
result[key] = Immutable(value[key])
50+
}
51+
return globalThis.Object.freeze(result)
52+
}
53+
/** Specialized deep immutable value. Applies freeze recursively to the given value */
54+
export function Immutable<T>(value: T): T {
55+
return ValueGuard.IsArray(value)
56+
? ImmutableArray(value)
57+
: ValueGuard.IsDate(value)
58+
? ImmutableDate(value)
59+
: ValueGuard.IsUint8Array(value)
60+
? ImmutableUint8Array(value)
61+
: ValueGuard.IsRegExp(value)
62+
? ImmutableRegExp(value)
63+
: ValueGuard.IsObject(value)
64+
? ImmutableObject(value)
65+
: value
66+
}

0 commit comments

Comments
 (0)