Skip to content

Commit

Permalink
Revision 0.33.0 (#941)
Browse files Browse the repository at this point in the history
* Implement Immutable Types

* Update TypeScript

* Support Clone and Freeze Instancing

* Ensure Options Cloned on Mapped Key and Result

* Revision 0.33.0
  • Loading branch information
sinclairzx81 authored Aug 7, 2024
1 parent e686997 commit 333c2a1
Show file tree
Hide file tree
Showing 89 changed files with 521 additions and 410 deletions.
2 changes: 1 addition & 1 deletion hammer.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export async function benchmark() {
// Test
// -------------------------------------------------------------------------------
export async function test_typescript() {
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']) {
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']) {
await shell(`npm install typescript@${version} --no-save`)
await test_static()
}
Expand Down
18 changes: 9 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@sinclair/typebox",
"version": "0.32.35",
"version": "0.33.0",
"description": "Json Schema Type Builder with Static Type Resolution for TypeScript",
"keywords": [
"typescript",
Expand Down Expand Up @@ -37,6 +37,6 @@
"ajv-formats": "^2.1.1",
"mocha": "^10.4.0",
"prettier": "^2.7.1",
"typescript": "^5.5.3"
"typescript": "^5.5.4"
}
}
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ THE SOFTWARE.
// Infrastructure
// ------------------------------------------------------------------
export * from './type/clone/index'
export * from './type/create/index'
export * from './type/error/index'
export * from './type/guard/index'
export * from './type/helpers/index'
Expand Down
15 changes: 13 additions & 2 deletions src/system/policy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,20 @@ import { IsObject, IsArray, IsNumber, IsUndefined } from '../value/guard/index'

export namespace TypeSystemPolicy {
// ------------------------------------------------------------------
// TypeSystemPolicy
// TypeSystemPolicy: Instancing
// ------------------------------------------------------------------
/**
* Configures the instantiation behavior of TypeBox types. The `default` option assigns raw JavaScript
* references for embedded types, which may cause side effects if type properties are explicitly updated
* outside the TypeBox type builder. The `clone` option creates copies of any shared types upon creation,
* preventing unintended side effects. The `freeze` option applies `Object.freeze()` to the type, making
* it fully readonly and immutable. Implementations should use `default` whenever possible, as it is the
* fastest way to instantiate types. The default setting is `default`.
*/
export let InstanceMode: 'default' | 'clone' | 'freeze' = 'default'
// ------------------------------------------------------------------
// TypeSystemPolicy: Checking
// ------------------------------------------------------------------
/** Shared assertion routines used by the value and errors modules */
/** Sets whether TypeBox should assert optional properties using the TypeScript `exactOptionalPropertyTypes` assertion policy. The default is `false` */
export let ExactOptionalPropertyTypes: boolean = false
/** Sets whether arrays should be treated as a kind of objects. The default is `false` */
Expand Down
5 changes: 3 additions & 2 deletions src/type/any/any.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ THE SOFTWARE.
---------------------------------------------------------------------------*/

import { CreateType } from '../create/index'
import type { TSchema, SchemaOptions } from '../schema/index'
import { Kind } from '../symbols/index'

Expand All @@ -35,6 +36,6 @@ export interface TAny extends TSchema {
}

/** `[Json]` Creates an Any type */
export function Any(options: SchemaOptions = {}): TAny {
return { ...options, [Kind]: 'Any' } as never
export function Any(options?: SchemaOptions): TAny {
return CreateType({ [Kind]: 'Any' }, options) as never
}
11 changes: 3 additions & 8 deletions src/type/array/array.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ THE SOFTWARE.
---------------------------------------------------------------------------*/

import { CloneType } from '../clone/type'
import { CreateType } from '../create/type'
import { Ensure } from '../helpers/index'
import type { SchemaOptions, TSchema } from '../schema/index'
import type { Static } from '../static/index'
Expand Down Expand Up @@ -54,11 +54,6 @@ export interface TArray<T extends TSchema = TSchema> extends TSchema, ArrayOptio
items: T
}
/** `[Json]` Creates an Array type */
export function Array<T extends TSchema>(schema: T, options: ArrayOptions = {}): TArray<T> {
return {
...options,
[Kind]: 'Array',
type: 'array',
items: CloneType(schema),
} as never
export function Array<T extends TSchema>(items: T, options?: ArrayOptions): TArray<T> {
return CreateType({ [Kind]: 'Array', type: 'array', items }, options) as never
}
11 changes: 3 additions & 8 deletions src/type/async-iterator/async-iterator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ THE SOFTWARE.
import type { TSchema, SchemaOptions } from '../schema/index'
import type { Static } from '../static/index'
import { Kind } from '../symbols/index'
import { CloneType } from '../clone/type'
import { CreateType } from '../create/type'

export interface TAsyncIterator<T extends TSchema = TSchema> extends TSchema {
[Kind]: 'AsyncIterator'
Expand All @@ -38,11 +38,6 @@ export interface TAsyncIterator<T extends TSchema = TSchema> extends TSchema {
items: T
}
/** `[JavaScript]` Creates a AsyncIterator type */
export function AsyncIterator<T extends TSchema>(items: T, options: SchemaOptions = {}): TAsyncIterator<T> {
return {
...options,
[Kind]: 'AsyncIterator',
type: 'AsyncIterator',
items: CloneType(items),
} as never
export function AsyncIterator<T extends TSchema>(items: T, options?: SchemaOptions): TAsyncIterator<T> {
return CreateType({ [Kind]: 'AsyncIterator', type: 'AsyncIterator', items }, options) as never
}
6 changes: 3 additions & 3 deletions src/type/awaited/awaited.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import type { TSchema, SchemaOptions } from '../schema/index'
import { Intersect, type TIntersect } from '../intersect/index'
import { Union, type TUnion } from '../union/index'
import { type TPromise } from '../promise/index'
import { CloneType } from '../clone/type'
import { CreateType } from '../create/type'

// ------------------------------------------------------------------
// TypeGuard
Expand Down Expand Up @@ -96,6 +96,6 @@ export type TAwaited<T extends TSchema> =
T extends TPromise<infer S> ? TAwaited<S> :
T
/** `[JavaScript]` Constructs a type by recursively unwrapping Promise types */
export function Awaited<T extends TSchema>(T: T, options: SchemaOptions = {}): TAwaited<T> {
return CloneType(AwaitedResolve(T), options)
export function Awaited<T extends TSchema>(T: T, options?: SchemaOptions): TAwaited<T> {
return CreateType(AwaitedResolve(T), options) as never
}
9 changes: 3 additions & 6 deletions src/type/bigint/bigint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ THE SOFTWARE.

import type { TSchema, SchemaOptions } from '../schema/index'
import { Kind } from '../symbols/index'
import { CreateType } from '../create/index'

export interface BigIntOptions extends SchemaOptions {
exclusiveMaximum?: bigint
Expand All @@ -42,10 +43,6 @@ export interface TBigInt extends TSchema, BigIntOptions {
type: 'bigint'
}
/** `[JavaScript]` Creates a BigInt type */
export function BigInt(options: BigIntOptions = {}): TBigInt {
return {
...options,
[Kind]: 'BigInt',
type: 'bigint',
} as never
export function BigInt(options?: BigIntOptions): TBigInt {
return CreateType({ [Kind]: 'BigInt', type: 'bigint' }, options) as never
}
9 changes: 3 additions & 6 deletions src/type/boolean/boolean.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,14 @@ THE SOFTWARE.

import type { TSchema, SchemaOptions } from '../schema/index'
import { Kind } from '../symbols/index'
import { CreateType } from '../create/index'

export interface TBoolean extends TSchema {
[Kind]: 'Boolean'
static: boolean
type: 'boolean'
}
/** `[Json]` Creates a Boolean type */
export function Boolean(options: SchemaOptions = {}): TBoolean {
return {
...options,
[Kind]: 'Boolean',
type: 'boolean',
} as never
export function Boolean(options?: SchemaOptions): TBoolean {
return CreateType({ [Kind]: 'Boolean', type: 'boolean' }, options) as never
}
6 changes: 3 additions & 3 deletions src/type/clone/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,14 @@ THE SOFTWARE.
---------------------------------------------------------------------------*/

import type { TSchema, SchemaOptions } from '../schema/index'
import { TSchema, SchemaOptions } from '../schema/index'
import { Clone } from './value'

/** Clones a Rest */
export function CloneRest<T extends TSchema[]>(schemas: T): T {
return schemas.map((schema) => CloneType(schema)) as never
}
/** Clones a Type */
export function CloneType<T extends TSchema>(schema: T, options: SchemaOptions = {}): T {
return { ...Clone(schema), ...options }
export function CloneType<T extends TSchema>(schema: T, options?: SchemaOptions): T {
return options === undefined ? Clone(schema) : Clone({ ...options, ...schema })
}
2 changes: 1 addition & 1 deletion src/type/composite/composite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ type TCompositeEvaluate<
// prettier-ignore
export type TComposite<T extends TSchema[]> = TCompositeEvaluate<T>
// prettier-ignore
export function Composite<T extends TSchema[]>(T: [...T], options: ObjectOptions = {}): TComposite<T> {
export function Composite<T extends TSchema[]>(T: [...T], options?: ObjectOptions): TComposite<T> {
const K = CompositeKeys(T)
const P = CompositeProperties(T, K)
const R = Object(P, options)
Expand Down
6 changes: 3 additions & 3 deletions src/type/const/const.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ import { Readonly, type TReadonly } from '../readonly/index'
import { Undefined, type TUndefined } from '../undefined/index'
import { Uint8Array, type TUint8Array } from '../uint8array/index'
import { Unknown, type TUnknown } from '../unknown/index'
import { CloneType } from '../clone/index'
import { CreateType } from '../create/index'

// ------------------------------------------------------------------
// ValueGuard
Expand Down Expand Up @@ -130,6 +130,6 @@ function FromValue<T, Root extends boolean>(value: T, root: Root): FromValue<T,
export type TConst<T> = FromValue<T, true>

/** `[JavaScript]` Creates a readonly const type from the given value. */
export function Const</* const (not supported in 4.0) */ T>(T: T, options: SchemaOptions = {}): TConst<T> {
return CloneType(FromValue(T, true), options) as never
export function Const</* const (not supported in 4.0) */ T>(T: T, options?: SchemaOptions): TConst<T> {
return CreateType(FromValue(T, true), options) as never
}
5 changes: 2 additions & 3 deletions src/type/constructor-parameters/constructor-parameters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ import type { TSchema, SchemaOptions } from '../schema/index'
import type { Ensure } from '../helpers/index'
import type { TConstructor } from '../constructor/index'
import { Tuple, type TTuple } from '../tuple/index'
import { CloneRest } from '../clone/type'

// ------------------------------------------------------------------
// ConstructorParameters
Expand All @@ -41,6 +40,6 @@ export type TConstructorParameters<T extends TConstructor<TSchema[], TSchema>> =
)

/** `[JavaScript]` Extracts the ConstructorParameters from the given Constructor type */
export function ConstructorParameters<T extends TConstructor<TSchema[], TSchema>>(schema: T, options: SchemaOptions = {}): TConstructorParameters<T> {
return Tuple(CloneRest(schema.parameters), { ...options })
export function ConstructorParameters<T extends TConstructor<TSchema[], TSchema>>(schema: T, options?: SchemaOptions): TConstructorParameters<T> {
return Tuple(schema.parameters, options)
}
10 changes: 2 additions & 8 deletions src/type/constructor/constructor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import type { Ensure } from '../helpers/index'
import type { TReadonlyOptional } from '../readonly-optional/index'
import type { TReadonly } from '../readonly/index'
import type { TOptional } from '../optional/index'
import { CloneType, CloneRest } from '../clone/type'
import { CreateType } from '../create/type'
import { Kind } from '../symbols/index'

// ------------------------------------------------------------------
Expand Down Expand Up @@ -66,11 +66,5 @@ export interface TConstructor<T extends TSchema[] = TSchema[], U extends TSchema
}
/** `[JavaScript]` Creates a Constructor type */
export function Constructor<T extends TSchema[], U extends TSchema>(parameters: [...T], returns: U, options?: SchemaOptions): TConstructor<T, U> {
return {
...options,
[Kind]: 'Constructor',
type: 'Constructor',
parameters: CloneRest(parameters),
returns: CloneType(returns),
} as never
return CreateType({ [Kind]: 'Constructor', type: 'Constructor', parameters, returns }, options) as never
}
66 changes: 66 additions & 0 deletions src/type/create/immutable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*--------------------------------------------------------------------------
@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 * as ValueGuard from '../guard/value'

function ImmutableArray(value: unknown[]) {
return globalThis.Object.freeze(value as any).map((value: unknown) => Immutable(value as any))
}
function ImmutableDate(value: Date) {
return value
}
function ImmutableUint8Array(value: Uint8Array) {
return value
}
function ImmutableRegExp(value: RegExp) {
return value
}
function ImmutableObject(value: Record<keyof any, unknown>) {
const result = {} as Record<PropertyKey, unknown>
for (const key of Object.getOwnPropertyNames(value)) {
result[key] = Immutable(value[key])
}
for (const key of Object.getOwnPropertySymbols(value)) {
result[key] = Immutable(value[key])
}
return globalThis.Object.freeze(result)
}
/** Specialized deep immutable value. Applies freeze recursively to the given value */
export function Immutable<T>(value: T): T {
return ValueGuard.IsArray(value)
? ImmutableArray(value)
: ValueGuard.IsDate(value)
? ImmutableDate(value)
: ValueGuard.IsUint8Array(value)
? ImmutableUint8Array(value)
: ValueGuard.IsRegExp(value)
? ImmutableRegExp(value)
: ValueGuard.IsObject(value)
? ImmutableObject(value)
: value
}
Loading

0 comments on commit 333c2a1

Please sign in to comment.