Skip to content

Commit

Permalink
Revision 0.34.22 (#1171)
Browse files Browse the repository at this point in the history
* Intrinsics Passthrough on Required and Partial

* ChangeLog

* Version
  • Loading branch information
sinclairzx81 authored Feb 14, 2025
1 parent 1bd8f78 commit 4e97af2
Show file tree
Hide file tree
Showing 11 changed files with 263 additions and 29 deletions.
2 changes: 2 additions & 0 deletions changelog/0.34.0.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
### 0.34.0
- [Revision 0.34.22](https://github.com/sinclairzx81/typebox/pull/1171)
- Intrinsic Types Number, String, Boolean, etc Passthrough on Required and Partial Mapping
- [Revision 0.34.21](https://github.com/sinclairzx81/typebox/pull/1168)
- Reimplement Computed Record Types
- [Revision 0.34.20](https://github.com/sinclairzx81/typebox/pull/1167)
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

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

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@sinclair/typebox",
"version": "0.34.21",
"version": "0.34.22",
"description": "Json Schema Type Builder with Static Type Resolution for TypeScript",
"keywords": [
"typescript",
Expand Down
72 changes: 53 additions & 19 deletions src/type/partial/partial.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,24 @@ import { type TObject, type TProperties, Object } from '../object/index'
import { type TIntersect, Intersect } from '../intersect/index'
import { type TUnion, Union } from '../union/index'
import { type TRef, Ref } from '../ref/index'
import { type TBigInt } from '../bigint/index'
import { type TBoolean } from '../boolean/index'
import { type TInteger } from '../integer/index'
import { type TLiteral } from '../literal/index'
import { type TNull } from '../null/index'
import { type TNumber } from '../number/index'
import { type TString } from '../string/index'
import { type TSymbol } from '../symbol/index'
import { type TUndefined } from '../undefined/index'

import { Discard } from '../discard/index'
import { TransformKind } from '../symbols/index'
import { PartialFromMappedResult, type TPartialFromMappedResult } from './partial-from-mapped-result'

// ------------------------------------------------------------------
// TypeGuard
// KindGuard
// ------------------------------------------------------------------
import { IsMappedResult, IsIntersect, IsUnion, IsObject, IsRef, IsComputed } from '../guard/kind'
import * as KindGuard from '../guard/kind'

// ------------------------------------------------------------------
// FromComputed
Expand Down Expand Up @@ -95,9 +105,9 @@ type TFromObject<Type extends TObject, Properties extends TProperties = Type['pr
TFromProperties<Properties>
)>>
// prettier-ignore
function FromObject<Type extends TObject>(T: Type): TFromObject<Type> {
const options = Discard(T, [TransformKind, '$id', 'required', 'properties'])
const properties = FromProperties(T['properties'])
function FromObject<Type extends TObject>(type: Type): TFromObject<Type> {
const options = Discard(type, [TransformKind, '$id', 'required', 'properties'])
const properties = FromProperties(type['properties'])
return Object(properties, options) as never
}
// ------------------------------------------------------------------
Expand All @@ -119,34 +129,58 @@ function FromRest<Types extends TSchema[]>(types: [...Types]): TFromRest<Types>
// prettier-ignore
function PartialResolve<Type extends TSchema>(type: Type): TPartial<Type> {
return (
IsComputed(type) ? FromComputed(type.target, type.parameters) :
IsRef(type) ? FromRef(type.$ref) :
IsIntersect(type) ? Intersect(FromRest(type.allOf)) :
IsUnion(type) ? Union(FromRest(type.anyOf)) :
IsObject(type) ? FromObject(type) :
// Mappable
KindGuard.IsComputed(type) ? FromComputed(type.target, type.parameters) :
KindGuard.IsRef(type) ? FromRef(type.$ref) :
KindGuard.IsIntersect(type) ? Intersect(FromRest(type.allOf)) :
KindGuard.IsUnion(type) ? Union(FromRest(type.anyOf)) :
KindGuard.IsObject(type) ? FromObject(type) :
// Intrinsic
KindGuard.IsBigInt(type) ? type :
KindGuard.IsBoolean(type) ? type :
KindGuard.IsInteger(type) ? type :
KindGuard.IsLiteral(type) ? type :
KindGuard.IsNull(type) ? type :
KindGuard.IsNumber(type) ? type :
KindGuard.IsString(type) ? type :
KindGuard.IsSymbol(type) ? type :
KindGuard.IsUndefined(type) ? type :
// Passthrough
Object({})
) as never
}
// ------------------------------------------------------------------
// TPartial
// ------------------------------------------------------------------
// prettier-ignore
export type TPartial<T extends TSchema> = (
T extends TRecursive<infer Type extends TSchema> ? TRecursive<TPartial<Type>> :
T extends TComputed<infer Target extends string, infer Parameters extends TSchema[]> ? TFromComputed<Target, Parameters> :
T extends TRef<infer Ref extends string> ? TFromRef<Ref> :
T extends TIntersect<infer Types extends TSchema[]> ? TIntersect<TFromRest<Types>> :
T extends TUnion<infer Types extends TSchema[]> ? TUnion<TFromRest<Types>> :
T extends TObject<infer Properties extends TProperties> ? TFromObject<TObject<Properties>> :
export type TPartial<Type extends TSchema> = (
// Mappable
Type extends TRecursive<infer Type extends TSchema> ? TRecursive<TPartial<Type>> :
Type extends TComputed<infer Target extends string, infer Parameters extends TSchema[]> ? TFromComputed<Target, Parameters> :
Type extends TRef<infer Ref extends string> ? TFromRef<Ref> :
Type extends TIntersect<infer Types extends TSchema[]> ? TIntersect<TFromRest<Types>> :
Type extends TUnion<infer Types extends TSchema[]> ? TUnion<TFromRest<Types>> :
Type extends TObject<infer Properties extends TProperties> ? TFromObject<TObject<Properties>> :
// Intrinsic
Type extends TBigInt ? Type :
Type extends TBoolean ? Type :
Type extends TInteger ? Type :
Type extends TLiteral ? Type :
Type extends TNull ? Type :
Type extends TNumber ? Type :
Type extends TString ? Type :
Type extends TSymbol ? Type :
Type extends TUndefined ? Type :
// Passthrough
TObject<{}>
)
/** `[Json]` Constructs a type where all properties are optional */
export function Partial<MappedResult extends TMappedResult>(type: MappedResult, options?: SchemaOptions): TPartialFromMappedResult<MappedResult>
/** `[Json]` Constructs a type where all properties are optional */
export function Partial<Type extends TSchema>(type: Type, options?: SchemaOptions): TPartial<Type>
/** `[Json]` Constructs a type where all properties are optional */
export function Partial(type: TSchema, options?: SchemaOptions): any {
if (IsMappedResult(type)) {
export function Partial(type: TSchema, options?: SchemaOptions): unknown {
if (KindGuard.IsMappedResult(type)) {
return PartialFromMappedResult(type, options)
} else {
// special: mapping types require overridable options
Expand Down
48 changes: 41 additions & 7 deletions src/type/required/required.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,24 @@ import { type TObject, type TProperties, Object } from '../object/index'
import { type TIntersect, Intersect } from '../intersect/index'
import { type TUnion, Union } from '../union/index'
import { type TRef, Ref } from '../ref/index'
import { type TBigInt } from '../bigint/index'
import { type TBoolean } from '../boolean/index'
import { type TInteger } from '../integer/index'
import { type TLiteral } from '../literal/index'
import { type TNull } from '../null/index'
import { type TNumber } from '../number/index'
import { type TString } from '../string/index'
import { type TSymbol } from '../symbol/index'
import { type TUndefined } from '../undefined/index'

import { OptionalKind, TransformKind } from '../symbols/index'
import { Discard } from '../discard/index'
import { RequiredFromMappedResult, type TRequiredFromMappedResult } from './required-from-mapped-result'

// ------------------------------------------------------------------
// TypeGuard
// ------------------------------------------------------------------
import { IsMappedResult, IsIntersect, IsUnion, IsObject, IsRef, IsComputed } from '../guard/kind'
import * as KindGuard from '../guard/kind'

// ------------------------------------------------------------------
// FromComputed
Expand Down Expand Up @@ -119,11 +129,23 @@ function FromRest<Types extends TSchema[]>(types: [...Types]) : TFromRest<Types>
// prettier-ignore
function RequiredResolve<Type extends TSchema>(type: Type): TRequired<Type> {
return (
IsComputed(type) ? FromComputed(type.target, type.parameters) :
IsRef(type) ? FromRef(type.$ref) :
IsIntersect(type) ? Intersect(FromRest(type.allOf)) :
IsUnion(type) ? Union(FromRest(type.anyOf)) :
IsObject(type) ? FromObject(type) :
// Mappable
KindGuard.IsComputed(type) ? FromComputed(type.target, type.parameters) :
KindGuard.IsRef(type) ? FromRef(type.$ref) :
KindGuard.IsIntersect(type) ? Intersect(FromRest(type.allOf)) :
KindGuard.IsUnion(type) ? Union(FromRest(type.anyOf)) :
KindGuard.IsObject(type) ? FromObject(type) :
// Intrinsic
KindGuard.IsBigInt(type) ? type :
KindGuard.IsBoolean(type) ? type :
KindGuard.IsInteger(type) ? type :
KindGuard.IsLiteral(type) ? type :
KindGuard.IsNull(type) ? type :
KindGuard.IsNumber(type) ? type :
KindGuard.IsString(type) ? type :
KindGuard.IsSymbol(type) ? type :
KindGuard.IsUndefined(type) ? type :
// Passthrough
Object({})
) as never
}
Expand All @@ -132,12 +154,24 @@ function RequiredResolve<Type extends TSchema>(type: Type): TRequired<Type> {
// ------------------------------------------------------------------
// prettier-ignore
export type TRequired<Type extends TSchema> = (
// Mappable
Type extends TRecursive<infer Type extends TSchema> ? TRecursive<TRequired<Type>> :
Type extends TComputed<infer Target extends string, infer Parameters extends TSchema[]> ? TFromComputed<Target, Parameters> :
Type extends TRef<infer Ref extends string> ? TFromRef<Ref> :
Type extends TIntersect<infer Types extends TSchema[]> ? TIntersect<TFromRest<Types>> :
Type extends TUnion<infer Types extends TSchema[]> ? TUnion<TFromRest<Types>> :
Type extends TObject<infer Properties extends TProperties> ? TFromObject<TObject<Properties>> :
// Intrinsic
Type extends TBigInt ? Type :
Type extends TBoolean ? Type :
Type extends TInteger ? Type :
Type extends TLiteral ? Type :
Type extends TNull ? Type :
Type extends TNumber ? Type :
Type extends TString ? Type :
Type extends TSymbol ? Type :
Type extends TUndefined ? Type :
// Passthrough
TObject<{}>
)
/** `[Json]` Constructs a type where all properties are required */
Expand All @@ -146,7 +180,7 @@ export function Required<MappedResult extends TMappedResult>(type: MappedResult,
export function Required<Type extends TSchema>(type: Type, options?: SchemaOptions): TRequired<Type>
/** `[Json]` Constructs a type where all properties are required */
export function Required<Type extends TSchema>(type: Type, options?: SchemaOptions): never {
if (IsMappedResult(type)) {
if (KindGuard.IsMappedResult(type)) {
return RequiredFromMappedResult(type, options) as never
} else {
// special: mapping types require overridable options
Expand Down
32 changes: 32 additions & 0 deletions test/runtime/type/guard/kind/partial.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,36 @@ describe('guard/kind/TPartial', () => {
const R = Type.Partial(S)
Assert.IsFalse(TransformKind in R)
})
// ------------------------------------------------------------------
// Intrinsic Passthough
// https://github.com/sinclairzx81/typebox/issues/1169
// ------------------------------------------------------------------
it('Should pass through on intrinsic types on union 1', () => {
const T = Type.Partial(
Type.Union([
Type.Number(),
Type.Object({
x: Type.Number(),
}),
]),
)
Assert.IsTrue(KindGuard.IsUnion(T))
Assert.IsTrue(KindGuard.IsNumber(T.anyOf[0]))
Assert.IsTrue(KindGuard.IsObject(T.anyOf[1]))
Assert.IsTrue(KindGuard.IsOptional(T.anyOf[1].properties.x))
})
it('Should pass through on intrinsic types on union 2', () => {
const T = Type.Partial(
Type.Union([
Type.Literal(1),
Type.Object({
x: Type.Number(),
}),
]),
)
Assert.IsTrue(KindGuard.IsUnion(T))
Assert.IsTrue(KindGuard.IsLiteral(T.anyOf[0]))
Assert.IsTrue(KindGuard.IsObject(T.anyOf[1]))
Assert.IsTrue(KindGuard.IsOptional(T.anyOf[1].properties.x))
})
})
32 changes: 32 additions & 0 deletions test/runtime/type/guard/kind/required.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,36 @@ describe('guard/kind/TRequired', () => {
const R = Type.Required(S)
Assert.IsFalse(TransformKind in R)
})
// ------------------------------------------------------------------
// Intrinsic Passthough
// https://github.com/sinclairzx81/typebox/issues/1169
// ------------------------------------------------------------------
it('Should pass through on intrinsic types on union 1', () => {
const T = Type.Required(
Type.Union([
Type.Number(),
Type.Object({
x: Type.Optional(Type.Number()),
}),
]),
)
Assert.IsTrue(KindGuard.IsUnion(T))
Assert.IsTrue(KindGuard.IsNumber(T.anyOf[0]))
Assert.IsTrue(KindGuard.IsObject(T.anyOf[1]))
Assert.IsFalse(KindGuard.IsOptional(T.anyOf[1].properties.x))
})
it('Should pass through on intrinsic types on union 2', () => {
const T = Type.Required(
Type.Union([
Type.Literal(1),
Type.Object({
x: Type.Optional(Type.Number()),
}),
]),
)
Assert.IsTrue(KindGuard.IsUnion(T))
Assert.IsTrue(KindGuard.IsLiteral(T.anyOf[0]))
Assert.IsTrue(KindGuard.IsObject(T.anyOf[1]))
Assert.IsFalse(KindGuard.IsOptional(T.anyOf[1].properties.x))
})
})
32 changes: 32 additions & 0 deletions test/runtime/type/guard/type/partial.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,4 +73,36 @@ describe('guard/type/TPartial', () => {
Assert.IsEqual(A.title, 'A')
Assert.IsEqual(B.title, 'B')
})
// ------------------------------------------------------------------
// Intrinsic Passthough
// https://github.com/sinclairzx81/typebox/issues/1169
// ------------------------------------------------------------------
it('Should pass through on intrinsic types on union 1', () => {
const T = Type.Partial(
Type.Union([
Type.Number(),
Type.Object({
x: Type.Number(),
}),
]),
)
Assert.IsTrue(TypeGuard.IsUnion(T))
Assert.IsTrue(TypeGuard.IsNumber(T.anyOf[0]))
Assert.IsTrue(TypeGuard.IsObject(T.anyOf[1]))
Assert.IsTrue(TypeGuard.IsOptional(T.anyOf[1].properties.x))
})
it('Should pass through on intrinsic types on union 2', () => {
const T = Type.Partial(
Type.Union([
Type.Literal(1),
Type.Object({
x: Type.Number(),
}),
]),
)
Assert.IsTrue(TypeGuard.IsUnion(T))
Assert.IsTrue(TypeGuard.IsLiteral(T.anyOf[0]))
Assert.IsTrue(TypeGuard.IsObject(T.anyOf[1]))
Assert.IsTrue(TypeGuard.IsOptional(T.anyOf[1].properties.x))
})
})
32 changes: 32 additions & 0 deletions test/runtime/type/guard/type/required.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,36 @@ describe('guard/type/TRequired', () => {
Assert.IsEqual(A.title, 'A')
Assert.IsEqual(B.title, 'B')
})
// ------------------------------------------------------------------
// Intrinsic Passthough
// https://github.com/sinclairzx81/typebox/issues/1169
// ------------------------------------------------------------------
it('Should pass through on intrinsic types on union 1', () => {
const T = Type.Required(
Type.Union([
Type.Number(),
Type.Object({
x: Type.Optional(Type.Number()),
}),
]),
)
Assert.IsTrue(TypeGuard.IsUnion(T))
Assert.IsTrue(TypeGuard.IsNumber(T.anyOf[0]))
Assert.IsTrue(TypeGuard.IsObject(T.anyOf[1]))
Assert.IsFalse(TypeGuard.IsOptional(T.anyOf[1].properties.x))
})
it('Should pass through on intrinsic types on union 2', () => {
const T = Type.Required(
Type.Union([
Type.Literal(1),
Type.Object({
x: Type.Optional(Type.Number()),
}),
]),
)
Assert.IsTrue(TypeGuard.IsUnion(T))
Assert.IsTrue(TypeGuard.IsLiteral(T.anyOf[0]))
Assert.IsTrue(TypeGuard.IsObject(T.anyOf[1]))
Assert.IsFalse(TypeGuard.IsOptional(T.anyOf[1].properties.x))
})
})
Loading

0 comments on commit 4e97af2

Please sign in to comment.