Revision 0.33.8 (#983)

* Prevent Intersect Transform Encode callback from being called twice

* Make strict the Encode and Decode return type

* Support default annotation being assigned Functions for lazy value initialization on Create

*  Enable Mapping types to override user defined options from source type

* Support Constraint Copy for Pick, Omit (inline with Partial / Required) (Trialing Implementation)

* Flag Strict For Deprecation
This commit is contained in:
sinclairzx81
2024-09-03 04:28:33 +09:00
committed by GitHub
parent 1aee5c0863
commit ed4c89a9ab
36 changed files with 307 additions and 257 deletions

View File

@@ -1,3 +1,42 @@
### 0.32.0
- [Revision 0.32.35](https://github.com/sinclairzx81/typebox/pull/914) Support Any for Record keys, Revert error message on required property, Fix order dependency for Union Convert.
- [Revision 0.32.34](https://github.com/sinclairzx81/typebox/pull/914) Fix template literal generation for template literals embedded within template literals.
- [Revision 0.32.33](https://github.com/sinclairzx81/typebox/pull/905) Pin ESM compiler target to ES2020.
- [Revision 0.32.32](https://github.com/sinclairzx81/typebox/pull/898) Fix for Enum properties when used with Mapped types.
- [Revision 0.32.31](https://github.com/sinclairzx81/typebox/pull/881) Fix for Cast. Dereference Union variants before scoring.
- [Revision 0.32.30](https://github.com/sinclairzx81/typebox/pull/868) Support null object prototypes for Encode/Decode.
- [Revision 0.32.29](https://github.com/sinclairzx81/typebox/pull/862) Key derive optimization to improve Intersect Encode/Decode performance.
- [Revision 0.32.28](https://github.com/sinclairzx81/typebox/pull/861) Fix for TransformEncode introduced with 0.32.24, 0.32.25 optimizations.
- [Revision 0.32.27](https://github.com/sinclairzx81/typebox/pull/854) Support for esm.sh and general build tooling updates.
- [Revision 0.32.26](https://github.com/sinclairzx81/typebox/pull/851) Optimization for number checks, use Number.isFinite(x) over typeof `number`.
- [Revision 0.32.25](https://github.com/sinclairzx81/typebox/pull/849) Optimizations for type builder to improve schema creation performance for computed types.
- [Revision 0.32.24](https://github.com/sinclairzx81/typebox/pull/848) Optimizations for Convert to avoid unnecessary object initialization and cloning.
- [Revision 0.32.22](https://github.com/sinclairzx81/typebox/pull/840) Add Support for Optional and Readonly Function and Constructor Arguments.
- [Revision 0.32.21](https://github.com/sinclairzx81/typebox/pull/836) Refactor Array Conversion logic. Discard TNever on TComposite.
- [Revision 0.32.20](https://github.com/sinclairzx81/typebox/pull/810) Fix compiler regression (TS 5.3.3 -> 5.4.2) generating Diff declaration structures.
- [Revision 0.32.19](https://github.com/sinclairzx81/typebox/pull/805) Revert Union Convert logic added on 0.32.16.
- [Revision 0.32.18](https://github.com/sinclairzx81/typebox/pull/801) Add explicit return type on TypeSystem.Type.
- [Revision 0.32.17](https://github.com/sinclairzx81/typebox/pull/799) Detect ambiguous inference for StaticDecode when inferring as any.
- [Revision 0.32.16](https://github.com/sinclairzx81/typebox/pull/791) Enhance Composite, Mapped, Indexed and Transform types. Intersect and Union Convert updates, Include Path in Validation Error.
- [Revision 0.32.15](https://github.com/sinclairzx81/typebox/pull/774) Additional internal guards for Type Arrays, Map and Set structures.
- [Revision 0.32.14](https://github.com/sinclairzx81/typebox/pull/753) Use barrel exports for submodules.
- [Revision 0.32.13](https://github.com/sinclairzx81/typebox/pull/744) Add minLength and maxLength constraint for RegExp
- [Revision 0.32.12](https://github.com/sinclairzx81/typebox/pull/740) Fix option assignment on Record types.
- [Revision 0.32.11](https://github.com/sinclairzx81/typebox/pull/738) Optimize Extract, Exclude. Overloads for Template Literal
- [Revision 0.32.10](https://github.com/sinclairzx81/typebox/pull/734) Export additional type infrastructure for Partial and Required
- [Revision 0.32.9](https://github.com/sinclairzx81/typebox/pull/731) Generalize Composite to accept schematics of type TSchema[]
- [Revision 0.32.8](https://github.com/sinclairzx81/typebox/pull/728) Ensure schema `default` annotation is cloned on Create.
- [Revision 0.32.7](https://github.com/sinclairzx81/typebox/pull/727) Ensure schema `default` annotation is cloned on Default.
- [Revision 0.32.6](https://github.com/sinclairzx81/typebox/pull/724) Export additional type infrastructure for mapping types
- [Revision 0.32.5](https://github.com/sinclairzx81/typebox/pull/718) Update licence year span for 2024
- [Revision 0.32.4](https://github.com/sinclairzx81/typebox/pull/708) Ensure ErrorFunctionParameter type is exported
- [Revision 0.32.3](https://github.com/sinclairzx81/typebox/pull/703) Simplify Record Static Type
- [Revision 0.32.1](https://github.com/sinclairzx81/typebox/pull/701) Specify default exports for Web Pack
## [0.32.0](https://www.npmjs.com/package/@sinclair/typebox/v/0.32.0)
## Overview

26
changelog/0.33.0.md Normal file
View File

@@ -0,0 +1,26 @@
### 0.33.0
- [Revision 0.33.8](https://github.com/sinclairzx81/typebox/pull/983)
- [982](https://github.com/sinclairzx81/typebox/issues/982) Prevent Intersect Transform Encode callback from being called twice
- [974](https://github.com/sinclairzx81/typebox/issues/974) Make strict the Encode and Decode return type
- [975](https://github.com/sinclairzx81/typebox/issues/975) Support default annotation being assigned Functions for lazy value initialization on Create
- [980](https://github.com/sinclairzx81/typebox/issues/980) Enable Mapping types to override user defined options from source type
- [976](https://github.com/sinclairzx81/typebox/issues/976) Support Constraint Copy for Pick, Omit (inline with Partial / Required) (Trialing Implementation)
- Flag Strict For Deprecation
- [Revision 0.33.7](https://github.com/sinclairzx81/typebox/pull/964)
- Additional updates to improve Default for enumerable objects.
- [Revision 0.33.6](https://github.com/sinclairzx81/typebox/pull/963)
- Add object traversal path for Default. Ensure enumerable objects are traversed.
- [Revision 0.33.5](https://github.com/sinclairzx81/typebox/pull/959)
- Provide better support for transforming properties with optional modifiers.
- [Revision 0.33.4](https://github.com/sinclairzx81/typebox/pull/953)
- Add Assert and Parse value functions. Add defacto AssertError type.
- [Revision 0.33.3](https://github.com/sinclairzx81/typebox/pull/950)
- Optimize Value Diff algorithm. Update edit sequence to INSERT, UPDATE then DELETE.
- [Revision 0.33.2](https://github.com/sinclairzx81/typebox/pull/947)
- Ensure user defined schema options are retained on mapping types, Pick, Omit and Mapped.
- [Revision 0.33.1](https://github.com/sinclairzx81/typebox/pull/945)
- Apply mutability fix for Intrinsic and Not schematics (inline with Default)
- [Revision 0.33.0](https://github.com/sinclairzx81/typebox/pull/941)
- Add InstanceMode to enable Clone, Freeze and Default schema initialization options. Optimize for Default.

View File

@@ -1,100 +0,0 @@
### 0.33.0
- [Revision 0.33.7](https://github.com/sinclairzx81/typebox/pull/964) Additional updates to improve Default for enumerable objects.
- [Revision 0.33.6](https://github.com/sinclairzx81/typebox/pull/963) Add object traversal path for Default. Ensure enumerable objects are traversed.
- [Revision 0.33.5](https://github.com/sinclairzx81/typebox/pull/959) Provide better support for transforming properties with optional modifiers.
- [Revision 0.33.4](https://github.com/sinclairzx81/typebox/pull/953) Add Assert and Parse value functions. Add defacto AssertError type.
- [Revision 0.33.3](https://github.com/sinclairzx81/typebox/pull/950) Optimize Value Diff algorithm. Update edit sequence to INSERT, UPDATE then DELETE.
- [Revision 0.33.2](https://github.com/sinclairzx81/typebox/pull/947) Ensure user defined schema options are retained on mapping types, Pick, Omit and Mapped.
- [Revision 0.33.1](https://github.com/sinclairzx81/typebox/pull/945) Apply mutability fix for Intrinsic and Not schematics (inline with Default)
- [Revision 0.33.0](https://github.com/sinclairzx81/typebox/pull/941) Add InstanceMode to enable Clone, Freeze and Default schema initialization options. Optimize for Default.
### 0.32.0
- [Revision 0.32.35](https://github.com/sinclairzx81/typebox/pull/914) Support Any for Record keys, Revert error message on required property, Fix order dependency for Union Convert.
- [Revision 0.32.34](https://github.com/sinclairzx81/typebox/pull/914) Fix template literal generation for template literals embedded within template literals.
- [Revision 0.32.33](https://github.com/sinclairzx81/typebox/pull/905) Pin ESM compiler target to ES2020.
- [Revision 0.32.32](https://github.com/sinclairzx81/typebox/pull/898) Fix for Enum properties when used with Mapped types.
- [Revision 0.32.31](https://github.com/sinclairzx81/typebox/pull/881) Fix for Cast. Dereference Union variants before scoring.
- [Revision 0.32.30](https://github.com/sinclairzx81/typebox/pull/868) Support null object prototypes for Encode/Decode.
- [Revision 0.32.29](https://github.com/sinclairzx81/typebox/pull/862) Key derive optimization to improve Intersect Encode/Decode performance.
- [Revision 0.32.28](https://github.com/sinclairzx81/typebox/pull/861) Fix for TransformEncode introduced with 0.32.24, 0.32.25 optimizations.
- [Revision 0.32.27](https://github.com/sinclairzx81/typebox/pull/854) Support for esm.sh and general build tooling updates.
- [Revision 0.32.26](https://github.com/sinclairzx81/typebox/pull/851) Optimization for number checks, use Number.isFinite(x) over typeof `number`.
- [Revision 0.32.25](https://github.com/sinclairzx81/typebox/pull/849) Optimizations for type builder to improve schema creation performance for computed types.
- [Revision 0.32.24](https://github.com/sinclairzx81/typebox/pull/848) Optimizations for Convert to avoid unnecessary object initialization and cloning.
- [Revision 0.32.22](https://github.com/sinclairzx81/typebox/pull/840) Add Support for Optional and Readonly Function and Constructor Arguments.
- [Revision 0.32.21](https://github.com/sinclairzx81/typebox/pull/836) Refactor Array Conversion logic. Discard TNever on TComposite.
- [Revision 0.32.20](https://github.com/sinclairzx81/typebox/pull/810) Fix compiler regression (TS 5.3.3 -> 5.4.2) generating Diff declaration structures.
- [Revision 0.32.19](https://github.com/sinclairzx81/typebox/pull/805) Revert Union Convert logic added on 0.32.16.
- [Revision 0.32.18](https://github.com/sinclairzx81/typebox/pull/801) Add explicit return type on TypeSystem.Type.
- [Revision 0.32.17](https://github.com/sinclairzx81/typebox/pull/799) Detect ambiguous inference for StaticDecode when inferring as any.
- [Revision 0.32.16](https://github.com/sinclairzx81/typebox/pull/791) Enhance Composite, Mapped, Indexed and Transform types. Intersect and Union Convert updates, Include Path in Validation Error.
- [Revision 0.32.15](https://github.com/sinclairzx81/typebox/pull/774) Additional internal guards for Type Arrays, Map and Set structures.
- [Revision 0.32.14](https://github.com/sinclairzx81/typebox/pull/753) Use barrel exports for submodules.
- [Revision 0.32.13](https://github.com/sinclairzx81/typebox/pull/744) Add minLength and maxLength constraint for RegExp
- [Revision 0.32.12](https://github.com/sinclairzx81/typebox/pull/740) Fix option assignment on Record types.
- [Revision 0.32.11](https://github.com/sinclairzx81/typebox/pull/738) Optimize Extract, Exclude. Overloads for Template Literal
- [Revision 0.32.10](https://github.com/sinclairzx81/typebox/pull/734) Export additional type infrastructure for Partial and Required
- [Revision 0.32.9](https://github.com/sinclairzx81/typebox/pull/731) Generalize Composite to accept schematics of type TSchema[]
- [Revision 0.32.8](https://github.com/sinclairzx81/typebox/pull/728) Ensure schema `default` annotation is cloned on Create.
- [Revision 0.32.7](https://github.com/sinclairzx81/typebox/pull/727) Ensure schema `default` annotation is cloned on Default.
- [Revision 0.32.6](https://github.com/sinclairzx81/typebox/pull/724) Export additional type infrastructure for mapping types
- [Revision 0.32.5](https://github.com/sinclairzx81/typebox/pull/718) Update licence year span for 2024
- [Revision 0.32.4](https://github.com/sinclairzx81/typebox/pull/708) Ensure ErrorFunctionParameter type is exported
- [Revision 0.32.3](https://github.com/sinclairzx81/typebox/pull/703) Simplify Record Static Type
- [Revision 0.32.1](https://github.com/sinclairzx81/typebox/pull/701) Specify default exports for Web Pack

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "@sinclair/typebox",
"version": "0.33.7",
"version": "0.33.8",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "@sinclair/typebox",
"version": "0.33.7",
"version": "0.33.8",
"license": "MIT",
"devDependencies": {
"@arethetypeswrong/cli": "^0.13.2",

View File

@@ -1,6 +1,6 @@
{
"name": "@sinclair/typebox",
"version": "0.33.7",
"version": "0.33.8",
"description": "Json Schema Type Builder with Static Type Resolution for TypeScript",
"keywords": [
"typescript",

View File

@@ -77,7 +77,6 @@ License MIT
- [Transform](#types-transform)
- [Guard](#types-guard)
- [Unsafe](#types-unsafe)
- [Strict](#types-strict)
- [Values](#values)
- [Assert](#values-assert)
- [Create](#values-create)
@@ -1078,35 +1077,6 @@ if(TypeGuard.IsString(T)) {
}
```
<a name='types-strict'></a>
### Strict
TypeBox types contain various symbol properties that are used for reflection, composition and compilation. These properties are not strictly valid Json Schema; so in some cases it may be desirable to omit them. TypeBox provides a `Strict` function that will omit these properties if necessary.
```typescript
const T = Type.Object({ // const T = {
name: Type.Optional(Type.String()) // [Symbol(TypeBox.Kind)]: 'Object',
}) // type: 'object',
// properties: {
// name: {
// type: 'string',
// [Symbol(TypeBox.Kind)]: 'String',
// [Symbol(TypeBox.Optional)]: 'Optional'
// }
// }
// }
const U = Type.Strict(T) // const U = {
// type: 'object',
// properties: {
// name: {
// type: 'string'
// }
// }
// }
```
<a name='values'></a>
## Values

View File

@@ -105,12 +105,12 @@ export class TypeCheck<T extends TSchema> {
return this.checkFunc(value)
}
/** Decodes a value or throws if error */
public Decode<R = StaticDecode<T>>(value: unknown): R {
public Decode<Static = StaticDecode<T>, Result extends Static = Static>(value: unknown): Result {
if (!this.checkFunc(value)) throw new TransformDecodeCheckError(this.schema, value, this.Errors(value).First()!)
return (this.hasTransform ? TransformDecode(this.schema, this.references, value) : value) as never
}
/** Encodes a value or throws if error */
public Encode<R = StaticEncode<T>>(value: unknown): R {
public Encode<Static = StaticDecode<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

View File

@@ -37,7 +37,7 @@ import { Kind } from '../symbols/index'
// ------------------------------------------------------------------
// TypeGuard
// ------------------------------------------------------------------
import { IsOptional, IsSchema } from '../guard/kind'
import { IsOptional } from '../guard/kind'
// ------------------------------------------------------------------
// ObjectStatic

View File

@@ -27,19 +27,18 @@ THE SOFTWARE.
---------------------------------------------------------------------------*/
import { CreateType } from '../create/type'
import type { TSchema, SchemaOptions } from '../schema/index'
import type { TupleToUnion, Evaluate } from '../helpers/index'
import { Discard } from '../discard/discard'
import type { SchemaOptions, TSchema } from '../schema/index'
import type { TupleToUnion, Evaluate, Ensure } from '../helpers/index'
import { type TRecursive } from '../recursive/index'
import type { TMappedKey, TMappedResult } from '../mapped/index'
import { Intersect, type TIntersect } from '../intersect/index'
import { Union, type TUnion } from '../union/index'
import { Object, type TObject, type TProperties } from '../object/index'
import { IndexPropertyKeys, type TIndexPropertyKeys } from '../indexed/index'
import { Discard } from '../discard/index'
import { TransformKind } from '../symbols/index'
import { OmitFromMappedKey, type TOmitFromMappedKey } from './omit-from-mapped-key'
import { OmitFromMappedResult, type TOmitFromMappedResult } from './omit-from-mapped-result'
import { TransformKind } from '../symbols/symbols'
// ------------------------------------------------------------------
// TypeGuard
// ------------------------------------------------------------------
@@ -86,6 +85,19 @@ function FromProperties<T extends TProperties, K extends PropertyKey[]>(T: T, K:
return K.reduce((T, K2) => FromProperty(T, K2), T as TProperties)
}
// ------------------------------------------------------------------
// FromObject
// ------------------------------------------------------------------
// prettier-ignore
type TFromObject<T extends TObject, K extends PropertyKey[], Properties extends TProperties = T['properties']> = Ensure<TObject<(
TFromProperties<Properties, K>
)>>
// prettier-ignore
function FromObject<T extends TObject, K extends PropertyKey[]>(T: T, K: K): TFromObject<T, K> {
const options = Discard(T, [TransformKind, '$id', 'required', 'properties'])
const properties = FromProperties(T['properties'], K)
return Object(properties, options) as never
}
// ------------------------------------------------------------------
// OmitResolve
// ------------------------------------------------------------------
// prettier-ignore
@@ -93,17 +105,18 @@ function OmitResolve<T extends TSchema, K extends PropertyKey[]>(T: T, K: [...K]
return (
IsIntersect(T) ? Intersect(FromIntersect(T.allOf, K)) :
IsUnion(T) ? Union(FromUnion(T.anyOf, K)) :
IsObject(T) ? Object(FromProperties(T.properties, K), Discard(T, [TransformKind, '$id', 'required'])) :
IsObject(T) ? FromObject(T, K) :
Object({})
) as never
}
// prettier-ignore
export type TOmit<T extends TProperties, K extends PropertyKey[]> =
T extends TRecursive<infer S> ? TRecursive<TOmit<S, K>> :
T extends TIntersect<infer S> ? TIntersect<TFromIntersect<S, K>> :
T extends TUnion<infer S> ? TUnion<TFromUnion<S, K>> :
T extends TObject<infer S> ? TObject<TFromProperties<S, K>> :
export type TOmit<T extends TProperties, K extends PropertyKey[]> = (
T extends TRecursive<infer S extends TSchema> ? TRecursive<TOmit<S, K>> :
T extends TIntersect<infer S extends TSchema[]> ? TIntersect<TFromIntersect<S, K>> :
T extends TUnion<infer S extends TSchema[]> ? TUnion<TFromUnion<S, K>> :
T extends TObject<infer S extends TProperties> ? TFromObject<TObject<S>, K> :
TObject<{}>
)
// ------------------------------------------------------------------
// TOmit
// ------------------------------------------------------------------
@@ -121,5 +134,6 @@ export function Omit(T: TSchema, K: any, options?: SchemaOptions): any {
if (IsMappedResult(T)) return OmitFromMappedResult(T, K, options)
// non-mapped
const I = IsSchema(K) ? IndexPropertyKeys(K) : (K as string[])
return CreateType(OmitResolve(T, I), options)
// special: mapping types require overridable options
return CreateType({ ...OmitResolve(T, I), ...options })
}

View File

@@ -28,7 +28,7 @@ THE SOFTWARE.
import { CreateType } from '../create/type'
import type { TSchema, SchemaOptions } from '../schema/index'
import type { Evaluate } from '../helpers/index'
import type { Evaluate, Ensure } from '../helpers/index'
import type { TMappedResult } from '../mapped/index'
import { type TReadonlyOptional } from '../readonly-optional/index'
import { type TOptional, Optional } from '../optional/index'
@@ -77,6 +77,19 @@ function FromProperties<T extends TProperties>(T: T): TFromProperties<T> {
return Acc as never
}
// ------------------------------------------------------------------
// FromObject
// ------------------------------------------------------------------
// prettier-ignore
type TFromObject<T extends TObject, Properties extends TProperties = T['properties']> = Ensure<TObject<(
TFromProperties<Properties>
)>>
// prettier-ignore
function FromObject<T extends TObject>(T: T): TFromObject<T> {
const options = Discard(T, [TransformKind, '$id', 'required', 'properties'])
const properties = FromProperties(T['properties'])
return Object(properties, options) as never
}
// ------------------------------------------------------------------
// PartialResolve
// ------------------------------------------------------------------
// prettier-ignore
@@ -84,7 +97,7 @@ function PartialResolve<T extends TSchema>(T: T): TPartial<T> {
return (
IsIntersect(T) ? Intersect(FromRest(T.allOf)) :
IsUnion(T) ? Union(FromRest(T.anyOf)) :
IsObject(T) ? Object(FromProperties(T.properties)) :
IsObject(T) ? FromObject(T) :
Object({})
) as never
}
@@ -93,10 +106,10 @@ function PartialResolve<T extends TSchema>(T: T): TPartial<T> {
// ------------------------------------------------------------------
// prettier-ignore
export type TPartial<T extends TSchema> = (
T extends TRecursive<infer S> ? TRecursive<TPartial<S>> :
T extends TIntersect<infer S> ? TIntersect<TFromRest<S>> :
T extends TUnion<infer S> ? TUnion<TFromRest<S>> :
T extends TObject<infer S> ? TObject<TFromProperties<S>> :
T extends TRecursive<infer S extends TSchema> ? TRecursive<TPartial<S>> :
T extends TIntersect<infer S extends TSchema[]> ? TIntersect<TFromRest<S>> :
T extends TUnion<infer S extends TSchema[]> ? TUnion<TFromRest<S>> :
T extends TObject<infer S extends TProperties> ? TFromObject<TObject<S>> :
TObject<{}>
)
/** `[Json]` Constructs a type where all properties are optional */
@@ -105,8 +118,10 @@ export function Partial<T extends TMappedResult>(T: T, options?: SchemaOptions):
export function Partial<T extends TSchema>(T: T, options?: SchemaOptions): TPartial<T>
/** `[Json]` Constructs a type where all properties are optional */
export function Partial(T: TSchema, options?: SchemaOptions): any {
if (IsMappedResult(T)) return PartialFromMappedResult(T, options)
const D = Discard(T, [TransformKind, '$id', 'required']) as TSchema
const R = PartialResolve(T)
return CreateType({ ...options, ...D, ...R }) as never
if (IsMappedResult(T)) {
return PartialFromMappedResult(T, options)
} else {
// special: mapping types require overridable options
return CreateType({ ...PartialResolve(T), ...options })
}
}

View File

@@ -27,18 +27,18 @@ THE SOFTWARE.
---------------------------------------------------------------------------*/
import { CreateType } from '../create/type'
import { Discard } from '../discard/discard'
import type { TSchema, SchemaOptions } from '../schema/index'
import type { TupleToUnion, Evaluate } from '../helpers/index'
import type { TupleToUnion, Evaluate, Ensure } from '../helpers/index'
import { type TRecursive } from '../recursive/index'
import { type TIntersect, Intersect } from '../intersect/index'
import { type TUnion, Union } from '../union/index'
import { type TObject, type TProperties, type TPropertyKey, Object } from '../object/index'
import type { TMappedKey, TMappedResult } from '../mapped/index'
import { IndexPropertyKeys, type TIndexPropertyKeys } from '../indexed/index'
import { Discard } from '../discard/index'
import { TransformKind } from '../symbols/index'
import { PickFromMappedKey, type TPickFromMappedKey } from './pick-from-mapped-key'
import { PickFromMappedResult, type TPickFromMappedResult } from './pick-from-mapped-result'
import { TransformKind } from '../symbols/symbols'
// ------------------------------------------------------------------
// TypeGuard
@@ -71,14 +71,27 @@ function FromUnion<T extends TSchema[], K extends PropertyKey[]>(T: T, K: K) {
// FromProperties
// ------------------------------------------------------------------
// prettier-ignore
type FromProperties<T extends TProperties, K extends PropertyKey[], I extends PropertyKey = TupleToUnion<K>> = Evaluate<Pick<T, I & keyof T>>
type TFromProperties<T extends TProperties, K extends PropertyKey[], I extends PropertyKey = TupleToUnion<K>> = Evaluate<Pick<T, I & keyof T>>
// prettier-ignore
function FromProperties<T extends TProperties, K extends PropertyKey[]>(T: T, K: K) {
function FromProperties<T extends TProperties, K extends PropertyKey[]>(T: T, K: K): TFromProperties <T, K> {
const Acc = {} as TProperties
for(const K2 of K) if(K2 in T) Acc[K2 as TPropertyKey] = T[K2 as keyof T]
return Acc as never
}
// ------------------------------------------------------------------
// FromObject
// ------------------------------------------------------------------
// prettier-ignore
type TFromObject<T extends TObject, K extends PropertyKey[], Properties extends TProperties = T['properties']> = Ensure<TObject<(
TFromProperties<Properties, K>
)>>
// prettier-ignore
function FromObject<T extends TObject, K extends PropertyKey[]>(T: T, K: K): TFromObject<T, K> {
const options = Discard(T, [TransformKind, '$id', 'required', 'properties'])
const properties = FromProperties(T['properties'], K)
return Object(properties, options) as never
}
// ------------------------------------------------------------------
// PickResolve
// ------------------------------------------------------------------
// prettier-ignore
@@ -86,16 +99,16 @@ function PickResolve<T extends TSchema, K extends PropertyKey[]>(T: T, K: [...K]
return (
IsIntersect(T) ? Intersect(FromIntersect(T.allOf, K)) :
IsUnion(T) ? Union(FromUnion(T.anyOf, K)) :
IsObject(T) ? Object(FromProperties(T.properties, K), Discard(T, [TransformKind, '$id', 'required'])) :
IsObject(T) ? FromObject(T, K) :
Object({})
) as never
}
// prettier-ignore
export type TPick<T extends TProperties, K extends PropertyKey[]> =
T extends TRecursive<infer S> ? TRecursive<TPick<S, K>> :
T extends TIntersect<infer S> ? TIntersect<FromIntersect<S, K>> :
T extends TUnion<infer S> ? TUnion<FromUnion<S, K>> :
T extends TObject<infer S> ? TObject<FromProperties<S, K>> :
T extends TRecursive<infer S extends TSchema> ? TRecursive<TPick<S, K>> :
T extends TIntersect<infer S extends TSchema[]> ? TIntersect<FromIntersect<S, K>> :
T extends TUnion<infer S extends TSchema[]> ? TUnion<FromUnion<S, K>> :
T extends TObject<infer S extends TProperties> ? TFromObject<TObject<S>, K> :
TObject<{}>
/** `[Json]` Constructs a type whose keys are picked from the given type */
@@ -112,5 +125,6 @@ export function Pick(T: TSchema, K: any, options?: SchemaOptions): any {
if (IsMappedResult(T)) return PickFromMappedResult(T, K, options)
// non-mapped
const I = IsSchema(K) ? IndexPropertyKeys(K) : (K as string[])
return CreateType(PickResolve(T, I), options)
// special: mapping types require overridable options
return CreateType({ ...PickResolve(T, I), ...options })
}

View File

@@ -28,7 +28,7 @@ THE SOFTWARE.
import { CreateType } from '../create/type'
import type { TSchema, SchemaOptions } from '../schema/index'
import type { Evaluate } from '../helpers/index'
import type { Evaluate, Ensure } from '../helpers/index'
import type { TMappedResult } from '../mapped/index'
import { type TReadonlyOptional } from '../readonly-optional/index'
import { type TOptional } from '../optional/index'
@@ -76,6 +76,19 @@ function FromProperties<T extends TProperties>(T: T) {
return Acc as never
}
// ------------------------------------------------------------------
// FromObject
// ------------------------------------------------------------------
// prettier-ignore
type TFromObject<T extends TObject, Properties extends TProperties = T['properties']> = Ensure<TObject<(
TFromProperties<Properties>
)>>
// prettier-ignore
function FromObject<T extends TObject>(T: T): TFromObject<T> {
const options = Discard(T, [TransformKind, '$id', 'required', 'properties'])
const properties = FromProperties(T['properties'])
return Object(properties, options) as never
}
// ------------------------------------------------------------------
// RequiredResolve
// ------------------------------------------------------------------
@@ -84,7 +97,7 @@ function RequiredResolve<T extends TSchema>(T: T): TRequired<T> {
return (
IsIntersect(T) ? Intersect(FromRest(T.allOf)) :
IsUnion(T) ? Union(FromRest(T.anyOf)) :
IsObject(T) ? Object(FromProperties(T.properties)) :
IsObject(T) ? FromObject(T) :
Object({})
) as never
}
@@ -93,10 +106,10 @@ function RequiredResolve<T extends TSchema>(T: T): TRequired<T> {
// ------------------------------------------------------------------
// prettier-ignore
export type TRequired<T extends TSchema> = (
T extends TRecursive<infer S> ? TRecursive<TRequired<S>> :
T extends TIntersect<infer S> ? TIntersect<TFromRest<S>> :
T extends TUnion<infer S> ? TUnion<TFromRest<S>> :
T extends TObject<infer S> ? TObject<TFromProperties<S>> :
T extends TRecursive<infer S extends TSchema> ? TRecursive<TRequired<S>> :
T extends TIntersect<infer S extends TSchema[]> ? TIntersect<TFromRest<S>> :
T extends TUnion<infer S extends TSchema[]> ? TUnion<TFromRest<S>> :
T extends TObject<infer S extends TProperties> ? TFromObject<TObject<S>> :
TObject<{}>
)
/** `[Json]` Constructs a type where all properties are required */
@@ -108,8 +121,7 @@ export function Required<T extends TSchema>(T: T, options?: SchemaOptions): neve
if (IsMappedResult(T)) {
return RequiredFromMappedResult(T, options) as never
} else {
const D = Discard(T, [TransformKind, '$id', 'required']) as TSchema
const R = RequiredResolve(T) as any
return CreateType({ ...D, ...R }, options) as never
// special: mapping types require overridable options
return CreateType({ ...RequiredResolve(T), ...options }) as never
}
}

View File

@@ -28,7 +28,17 @@ THE SOFTWARE.
import type { TSchema } from '../schema/index'
/** `[Json]` Omits compositing symbols from this schema. */
export function Strict<T extends TSchema>(schema: T): T {
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))
}

View File

@@ -61,7 +61,7 @@ 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 } from '../strict/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'
@@ -75,8 +75,16 @@ export class JsonTypeBuilder {
// ------------------------------------------------------------------------
// Strict
// ------------------------------------------------------------------------
/** `[Json]` Omits compositing symbols from this schema */
public Strict<T extends TSchema>(schema: T): 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()))
* ```
*/
public Strict<T extends TSchema>(schema: T): TStrict<T> {
return Strict(schema)
}
// ------------------------------------------------------------------------
@@ -242,7 +250,7 @@ export class JsonTypeBuilder {
/** `[Json]` Constructs a type whose keys are omitted from the given type */
public Omit<T extends TMappedResult, K extends PropertyKey[]>(T: T, K: [...K], options?: SchemaOptions): TOmitFromMappedResult<T, K>
/** `[Json]` Constructs a type whose keys are omitted from the given type */
public Omit<T extends TSchema, K extends TMappedKey>(T: T, K: K): TOmitFromMappedKey<T, K>
public Omit<T extends TSchema, K extends TMappedKey>(T: T, K: K, options?: SchemaOptions): TOmitFromMappedKey<T, K>
/** `[Json]` Constructs a type whose keys are omitted from the given type */
public Omit<T extends TSchema, K extends TSchema, I extends PropertyKey[] = TIndexPropertyKeys<K>>(T: T, K: K, options?: SchemaOptions): TOmit<T, I>
/** `[Json]` Constructs a type whose keys are omitted from the given type */
@@ -252,17 +260,17 @@ export class JsonTypeBuilder {
return Omit(schema, unresolved, options)
}
/** `[Json]` Constructs a type where all properties are optional */
public Partial<T extends TMappedResult>(T: T, options?: ObjectOptions): TPartialFromMappedResult<T>
public Partial<T extends TMappedResult>(T: T, options?: SchemaOptions): TPartialFromMappedResult<T>
/** `[Json]` Constructs a type where all properties are optional */
public Partial<T extends TSchema>(schema: T, options?: ObjectOptions): TPartial<T>
public Partial<T extends TSchema>(schema: T, options?: SchemaOptions): TPartial<T>
/** `[Json]` Constructs a type where all properties are optional */
public Partial(schema: TSchema, options?: ObjectOptions): any {
public Partial(schema: TSchema, options?: SchemaOptions): any {
return Partial(schema, options)
}
/** `[Json]` Constructs a type whose keys are picked from the given type */
public Pick<T extends TMappedResult, K extends PropertyKey[]>(T: T, K: [...K]): TPickFromMappedResult<T, K>
public Pick<T extends TMappedResult, K extends PropertyKey[]>(T: T, K: [...K], options?: SchemaOptions): TPickFromMappedResult<T, K>
/** `[Json]` Constructs a type whose keys are picked from the given type */
public Pick<T extends TSchema, K extends TMappedKey>(T: T, K: K): TPickFromMappedKey<T, K>
public Pick<T extends TSchema, K extends TMappedKey>(T: T, K: K, options?: SchemaOptions): TPickFromMappedKey<T, K>
/** `[Json]` Constructs a type whose keys are picked from the given type */
public Pick<T extends TSchema, K extends TSchema, I extends PropertyKey[] = TIndexPropertyKeys<K>>(T: T, K: K, options?: SchemaOptions): TPick<T, I>
/** `[Json]` Constructs a type whose keys are picked from the given type */
@@ -288,11 +296,11 @@ export class JsonTypeBuilder {
return Ref(unresolved as any, options)
}
/** `[Json]` Constructs a type where all properties are required */
public Required<T extends TMappedResult>(T: T, options?: ObjectOptions): TRequiredFromMappedResult<T>
public Required<T extends TMappedResult>(T: T, options?: SchemaOptions): TRequiredFromMappedResult<T>
/** `[Json]` Constructs a type where all properties are required */
public Required<T extends TSchema>(schema: T, options?: ObjectOptions): TRequired<T>
public Required<T extends TSchema>(schema: T, options?: SchemaOptions): TRequired<T>
/** `[Json]` Constructs a type where all properties are required */
public Required(schema: TSchema, options?: ObjectOptions): any {
public Required(schema: TSchema, options?: SchemaOptions): any {
return Required(schema, options)
}
/** `[Json]` Extracts interior Rest elements from Tuple, Intersect and Union types */

View File

@@ -75,8 +75,8 @@ export { RegExp } from '../regexp/index'
export { Required } from '../required/index'
export { Rest } from '../rest/index'
export { ReturnType } from '../return-type/index'
export { Strict } from '../strict/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'

View File

@@ -70,6 +70,8 @@ import type { TUndefined } from '../../type/undefined/index'
import type { TUint8Array } from '../../type/uint8array/index'
import type { TVoid } from '../../type/void/index'
import { IsFunction } from '../guard/guard'
// ------------------------------------------------------------------
// Errors
// ------------------------------------------------------------------
@@ -82,7 +84,7 @@ export class ValueCreateError extends TypeBoxError {
// Default
// ------------------------------------------------------------------
function FromDefault(value: unknown) {
return typeof value === 'function' ? value : Clone(value)
return IsFunction(value) ? value() : Clone(value)
}
// ------------------------------------------------------------------
// Create

View File

@@ -44,7 +44,7 @@ import type { TUnion } from '../../type/union/index'
// ------------------------------------------------------------------
// ValueGuard
// ------------------------------------------------------------------
import { IsString, IsObject, IsArray, IsUndefined, HasPropertyKey } from '../guard/index'
import { IsString, IsFunction, IsObject, IsArray, IsUndefined, HasPropertyKey } from '../guard/index'
// ------------------------------------------------------------------
// TypeGuard
// ------------------------------------------------------------------
@@ -53,7 +53,8 @@ import { IsKind } from '../../type/guard/kind'
// ValueOrDefault
// ------------------------------------------------------------------
function ValueOrDefault(schema: TSchema, value: unknown): unknown {
const clone = HasPropertyKey(schema, 'default') ? Clone(schema.default) : undefined
const defaultValue = HasPropertyKey(schema, 'default') ? schema.default : undefined
const clone = IsFunction(defaultValue) ? defaultValue() : Clone(defaultValue)
return IsUndefined(value) ? clone : IsObject(value) && IsObject(clone) ? Object.assign(clone, value) : value
}
// ------------------------------------------------------------------

View File

@@ -106,7 +106,7 @@ function FromIntersect(schema: TIntersect, references: TSchema[], path: string,
knownProperties[knownKey] = Visit(knownSchema, references, `${path}/${knownKey}`, knownProperties[knownKey])
}
if (!IsTransform(schema.unevaluatedProperties)) {
return Default(schema, path, knownProperties)
return knownProperties
}
const unknownKeys = Object.getOwnPropertyNames(knownProperties)
const unevaluatedProperties = schema.unevaluatedProperties as TSchema

View File

@@ -98,9 +98,9 @@ export function Clone<T>(value: T): T {
return CloneValue(value)
}
/** Decodes a value or throws if error */
export function Decode<T extends TSchema, R = StaticDecode<T>>(schema: T, references: TSchema[], value: unknown): R
export function Decode<T extends TSchema, Static = StaticDecode<T>, Result extends Static = Static>(schema: T, references: TSchema[], value: unknown): Result
/** Decodes a value or throws if error */
export function Decode<T extends TSchema, R = StaticDecode<T>>(schema: T, value: unknown): R
export function Decode<T extends TSchema, Static = StaticDecode<T>, Result extends Static = Static>(schema: T, value: unknown): Result
/** Decodes a value or throws if error */
export function Decode(...args: any[]): any {
const [schema, references, value] = args.length === 3 ? [args[0], args[1], args[2]] : [args[0], [], args[1]]
@@ -116,9 +116,9 @@ export function Default(...args: any[]): any {
return DefaultValue.apply(DefaultValue, args as any)
}
/** Encodes a value or throws if error */
export function Encode<T extends TSchema, R = StaticEncode<T>>(schema: T, references: TSchema[], value: unknown): R
export function Encode<T extends TSchema, Static = StaticEncode<T>, Result extends Static = Static>(schema: T, references: TSchema[], value: unknown): Result
/** Encodes a value or throws if error */
export function Encode<T extends TSchema, R = StaticEncode<T>>(schema: T, value: unknown): R
export function Encode<T extends TSchema, Static = StaticEncode<T>, Result extends Static = Static>(schema: T, value: unknown): Result
/** Encodes a value or throws if error */
export function Encode(...args: any[]): any {
const [schema, references, value] = args.length === 3 ? [args[0], args[1], args[2]] : [args[0], [], args[1]]

View File

@@ -52,6 +52,7 @@ describe('compiler-ajv/Pick', () => {
Assert.IsEqual(A.additionalProperties, false)
Assert.IsEqual(T.additionalProperties, false)
})
it('Should pick with keyof object', () => {
const A = Type.Object({
x: Type.Number(),

View File

@@ -44,6 +44,7 @@ describe('compiler-ajv/Required', () => {
Assert.IsEqual(A.additionalPropeties, false)
Assert.IsEqual(T.additionalPropeties, false)
})
// it('Should construct new object when targetting reference', () => {
// const T = Type.Object({ a: Type.String(), b: Type.String() }, { $id: 'T' })
// const R = Type.Ref(T)

View File

@@ -28,7 +28,6 @@ describe('compiler/Omit', () => {
const T = Type.Omit(A, ['z'])
strictEqual(T.required!.includes('z'), false)
})
it('Should inherit options from the source object', () => {
const A = Type.Object(
{
@@ -42,7 +41,6 @@ describe('compiler/Omit', () => {
strictEqual(A.additionalProperties, false)
strictEqual(T.additionalProperties, false)
})
it('Should omit with keyof object', () => {
const A = Type.Object({
x: Type.Number(),
@@ -58,15 +56,14 @@ describe('compiler/Omit', () => {
Fail(T, { x: 0, y: 0, z: 0 })
})
it('Should support Omit of Literal', () => {
const A = Type.Object(
{
x: Type.Number(),
y: Type.Number(),
z: Type.Number(),
},
{ additionalProperties: false },
)
const T = Type.Omit(A, Type.Literal('x'))
const A = Type.Object({
x: Type.Number(),
y: Type.Number(),
z: Type.Number(),
})
const T = Type.Omit(A, Type.Literal('x'), {
additionalProperties: false,
})
Ok(T, { y: 1, z: 1 })
Fail(T, { x: 1, y: 1, z: 1 })
})

View File

@@ -40,6 +40,7 @@ describe('compiler/Pick', () => {
strictEqual(A.additionalProperties, false)
strictEqual(T.additionalProperties, false)
})
it('Should pick with keyof object', () => {
const A = Type.Object({
x: Type.Number(),
@@ -55,28 +56,26 @@ describe('compiler/Pick', () => {
Fail(T, { x: 0, y: 0, z: 0 })
})
it('Should support Pick of Literal', () => {
const A = Type.Object(
{
x: Type.Number(),
y: Type.Number(),
z: Type.Number(),
},
{ additionalProperties: false },
)
const T = Type.Pick(A, Type.Literal('x'))
const A = Type.Object({
x: Type.Number(),
y: Type.Number(),
z: Type.Number(),
})
const T = Type.Pick(A, Type.Literal('x'), {
additionalProperties: false,
})
Ok(T, { x: 1 })
Fail(T, { x: 1, y: 1, z: 1 })
})
it('Should support Pick of Never', () => {
const A = Type.Object(
{
x: Type.Number(),
y: Type.Number(),
z: Type.Number(),
},
{ additionalProperties: false },
)
const T = Type.Pick(A, Type.Never())
const A = Type.Object({
x: Type.Number(),
y: Type.Number(),
z: Type.Number(),
})
const T = Type.Pick(A, Type.Never(), {
additionalProperties: false,
})
Fail(T, { x: 1, y: 1, z: 1 })
Ok(T, {})
})

View File

@@ -44,6 +44,7 @@ describe('compiler/Required', () => {
strictEqual(A.additionalPropeties, false)
strictEqual(T.additionalPropeties, false)
})
// it('Should construct new object when targetting reference', () => {
// const T = Type.Object({ a: Type.String(), b: Type.String() }, { $id: 'T' })
// const R = Type.Ref(T)

View File

@@ -157,4 +157,13 @@ describe('guard/type/TOmit', () => {
Assert.IsFalse(T.properties.y.additionalProperties as boolean)
Assert.IsFalse(T.properties.z.additionalProperties as boolean)
})
// ----------------------------------------------------------------
// https://github.com/sinclairzx81/typebox/issues/980
// ----------------------------------------------------------------
it('Should override properties in source type', () => {
const A = Type.Object({ x: Type.Number() }, { title: 'A' })
const B = Type.Omit(A, ['x'], { title: 'B' })
Assert.IsEqual(A.title, 'A')
Assert.IsEqual(B.title, 'B')
})
})

View File

@@ -64,4 +64,13 @@ describe('guard/type/TPartial', () => {
const R = Type.Partial(S)
Assert.IsFalse(TransformKind in R)
})
// ----------------------------------------------------------------
// https://github.com/sinclairzx81/typebox/issues/980
// ----------------------------------------------------------------
it('Should override properties in source type', () => {
const A = Type.Object({ x: Type.Number() }, { title: 'A' })
const B = Type.Partial(A, { title: 'B' })
Assert.IsEqual(A.title, 'A')
Assert.IsEqual(B.title, 'B')
})
})

View File

@@ -159,4 +159,13 @@ describe('guard/type/TPick', () => {
Assert.IsFalse(T.properties.y.additionalProperties as boolean)
Assert.IsFalse(T.properties.z.additionalProperties as boolean)
})
// ----------------------------------------------------------------
// https://github.com/sinclairzx81/typebox/issues/980
// ----------------------------------------------------------------
it('Should override properties in source type', () => {
const A = Type.Object({ x: Type.Number() }, { title: 'A' })
const B = Type.Pick(A, ['x'], { title: 'B' })
Assert.IsEqual(A.title, 'A')
Assert.IsEqual(B.title, 'B')
})
})

View File

@@ -61,4 +61,13 @@ describe('guard/type/TRequired', () => {
const R = Type.Required(S)
Assert.IsFalse(TransformKind in R)
})
// ----------------------------------------------------------------
// https://github.com/sinclairzx81/typebox/issues/980
// ----------------------------------------------------------------
it('Should override properties in source type', () => {
const A = Type.Object({ x: Type.Number() }, { title: 'A' })
const B = Type.Required(A, { title: 'B' })
Assert.IsEqual(A.title, 'A')
Assert.IsEqual(B.title, 'B')
})
})

View File

@@ -0,0 +1,11 @@
import { Value } from '@sinclair/typebox/value'
import { Type } from '@sinclair/typebox'
import { Assert } from '../../assert/index'
describe('value/create/Deferred', () => {
it('Should use deferred value', () => {
const T = Type.Any({ default: () => 1 })
const R = Value.Create(T)
Assert.IsEqual(R, 1)
})
})

View File

@@ -22,11 +22,12 @@ describe('value/create/Constructor', () => {
test: Type.Function([], Type.Number({ default: 123 })),
}),
{
default: class {
test() {
return 321
}
},
default: () =>
class {
test() {
return 321
}
},
},
)
const C = Value.Create(T)

View File

@@ -10,7 +10,7 @@ describe('value/create/Function', () => {
Assert.IsEqual(R, 123)
})
it('Should create default', () => {
const T = Type.Function([], Type.Number({ default: 123 }), { default: () => 321 })
const T = Type.Function([], Type.Number({ default: 123 }), { default: () => () => 321 })
const F = Value.Create(T)
const R = F()
Assert.IsEqual(R, 321)

View File

@@ -1,3 +1,4 @@
import './_deferred'
import './any'
import './array'
import './async-iterator'

View File

@@ -0,0 +1,11 @@
import { Value } from '@sinclair/typebox/value'
import { Type } from '@sinclair/typebox'
import { Assert } from '../../assert/index'
describe('value/default/Deferred', () => {
it('Should use deferred value', () => {
const T = Type.Any({ default: () => 1 })
const R = Value.Default(T, 1)
Assert.IsEqual(R, 1)
})
})

View File

@@ -1,3 +1,4 @@
import './_deferred'
import './any'
import './array'
import './async-iterator'

View File

@@ -44,7 +44,6 @@ import './regexp'
import './required'
import './rest'
import './return-type'
import './strict'
import './string'
import './symbol'
import './template-literal'

View File

@@ -1,20 +0,0 @@
import { Expect } from './assert'
import { Type, Static } from '@sinclair/typebox'
{
const T = Type.Strict(
Type.Object({
A: Type.String(),
B: Type.String(),
C: Type.String(),
}),
)
type T = Static<typeof T>
Expect(T).ToStatic<{
A: string
B: string
C: string
}>()
}