Revision 0.32.9 (#731)

* Optimize Composite

* Set and Composite Tests

* Version
This commit is contained in:
sinclairzx81
2024-01-13 00:01:44 +09:00
committed by GitHub
parent 9adcb58f22
commit dcf48f3232
16 changed files with 536 additions and 75 deletions

View File

@@ -56,7 +56,7 @@ export interface TBoolean extends Types.TSchema {
// --------------------------------------------------------------------------
export type InferUnion<T extends TStruct[], D extends string, Index = string> =
T extends [infer L extends TStruct, ...infer R extends TStruct[]]
? Types.Evaluate<{ [_ in D]: Index } & Types.Static<L>> | InferUnion<R, D, Types.Increment<Types.Assert<Index, string>>>
? Types.Evaluate<{ [_ in D]: Index } & Types.Static<L>> | InferUnion<R, D, Types.TIncrement<Types.Assert<Index, string>>>
: never
export interface TUnion<T extends TStruct[] = TStruct[], D extends string = string> extends Types.TSchema {

4
package-lock.json generated
View File

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

View File

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

View File

@@ -34,9 +34,37 @@ export { PatternBoolean, PatternBooleanExact, PatternNumber, PatternNumberExact,
export { TypeRegistry, FormatRegistry } from './type/registry/index'
export { TypeGuard, ValueGuard } from './type/guard/index'
export { CloneType, CloneRest } from './type/clone/type'
// ------------------------------------------------------------------
// Error
// ------------------------------------------------------------------
export { TypeBoxError } from './type/error/index'
// ------------------------------------------------------------------
// Type
// Sets
// ------------------------------------------------------------------
export {
SetComplement,
SetDistinct,
SetIncludes,
SetIntersect,
SetIntersectMany,
SetIsSubset,
SetUnion,
SetUnionMany,
type TSetComplement,
type TSetDistinct,
type TSetIncludes,
type TSetIntersect,
type TSetIntersectMany,
type TSetIsSubset,
type TSetUnion,
type TSetUnionMany,
} from './type/sets/index'
// ------------------------------------------------------------------
// Helpers
// ------------------------------------------------------------------
export { Increment, type TIncrement, type Assert, type AssertType, type AssertRest, type AssertProperties, type Ensure, type Evaluate, type TupleToIntersect, type TupleToUnion, type UnionToTuple } from './type/helpers/index'
// ------------------------------------------------------------------
// Types
// ------------------------------------------------------------------
export { Any, type TAny } from './type/any/index'
export { Array, type TArray, type ArrayOptions } from './type/array/index'
@@ -55,7 +83,6 @@ export { Exclude, type TExclude, type TExcludeFromMappedResult } from './type/ex
export { Extends, ExtendsCheck, ExtendsResult, ExtendsUndefinedCheck, type TExtends, type ExtendsFromMappedResult, type ExtendsFromMappedKey } from './type/extends/index'
export { Extract, type TExtract, type TExtractFromMappedResult } from './type/extract/index'
export { Function, type TFunction } from './type/function/index'
export { Increment, type Assert, type AssertType, type AssertRest, type AssertProperties, type Ensure, type Evaluate, type TupleToIntersect, type TupleToUnion, type UnionToTuple } from './type/helpers/index'
export {
Index,
IndexPropertyKeys,
@@ -102,7 +129,7 @@ export { type TSchema, type TKind, type SchemaOptions, type TAnySchema } from '.
export { type Static, type StaticDecode, type StaticEncode, type TDecodeType, type TDecodeRest, type TDecodeProperties } from './type/static/index'
export { Strict } from './type/strict/index'
export { String, type TString, type StringOptions, type StringFormatOption, type StringContentEncodingOption } from './type/string/index'
export { Symbol, type TSymbol, type TSymbolValue as SymbolValue } from './type/symbol/index'
export { Symbol, type TSymbol, type TSymbolValue } from './type/symbol/index'
export {
TemplateLiteral,
TemplateLiteralSyntax,

View File

@@ -27,44 +27,95 @@ THE SOFTWARE.
---------------------------------------------------------------------------*/
import type { TSchema } from '../schema/index'
import type { UnionToTuple, Assert, Ensure, Evaluate } from '../helpers/index'
import type { Evaluate } from '../helpers/index'
import { IntersectEvaluated, type TIntersectEvaluated } from '../intersect/index'
import { IndexFromPropertyKeys, type TIndexFromPropertyKeys } from '../indexed/index'
import { KeyOfPropertyKeys, type TKeyOfPropertyKeys } from '../keyof/index'
import { type TNever } from '../never/index'
import { Object, type TObject, type TProperties, type ObjectOptions } from '../object/index'
import { Intersect, type TIntersect } from '../intersect/index'
import { Index, type TIndex } from '../indexed/index'
import { KeyOfPropertyKeys } from '../keyof/index'
import { SetDistinct, TSetDistinct } from '../sets/index'
// ------------------------------------------------------------------
// TCompositeKeys
// TypeGuard
// ------------------------------------------------------------------
import { IsNever } from '../guard/type'
// ------------------------------------------------------------------
// CompositeKeys
// ------------------------------------------------------------------
// prettier-ignore
type TCompositeKeys<T extends TObject[], Acc extends PropertyKey = never> =
T extends [infer L extends TObject, ...infer R extends TObject[]]
? TCompositeKeys<R, Acc | keyof L['properties']>
type TCompositeKeys<T extends TSchema[], Acc extends PropertyKey[] = []> = (
T extends [infer L extends TSchema, ...infer R extends TSchema[]]
? TCompositeKeys<R, TSetDistinct<[...Acc, ...TKeyOfPropertyKeys<L>]>>
: Acc
// ------------------------------------------------------------------
// TCompositeIndex
// ------------------------------------------------------------------
)
// prettier-ignore
type TCompositeIndex<T extends TIntersect<TObject[]>, K extends string[], Acc extends TProperties = {}> =
K extends [infer L extends string, ...infer R extends string[]]
? TCompositeIndex<T, R, Acc & { [_ in L]: TIndex<T, [L]> }>
: Acc
// prettier-ignore
type TCompositeReduce<T extends TObject[]> = UnionToTuple<TCompositeKeys<T>> extends infer K
? Evaluate<TCompositeIndex<TIntersect<T>, Assert<K, string[]>>>
: {} // ^ indexed via intersection of T
// ------------------------------------------------------------------
// TComposite
// ------------------------------------------------------------------
// prettier-ignore
export type TComposite<T extends TObject[]> = TIntersect<T> extends TIntersect
? Ensure<TObject<TCompositeReduce<T>>>
: Ensure<TObject<{}>>
/** `[Json]` Creates a Composite object type */
export function Composite<T extends TObject[]>(T: [...T], options?: ObjectOptions): TComposite<T> {
const intersect: TSchema = Intersect(T, {})
const keys = KeyOfPropertyKeys(intersect) as string[]
const properties = keys.reduce((acc, key) => ({ ...acc, [key]: Index(intersect, [key]) }), {} as TProperties)
return Object(properties, options) as TComposite<T>
function CompositeKeys<T extends TSchema[]>(T: [...T]): TCompositeKeys<T> {
return T.reduce((Acc, L) => {
return SetDistinct([...Acc, ...KeyOfPropertyKeys(L)]) as never
}, []) as never
}
// ------------------------------------------------------------------
// FilterNever
// ------------------------------------------------------------------
// prettier-ignore
type TFilterNever<T extends TSchema[], Acc extends TSchema[] = []> = (
T extends [infer L extends TSchema, ...infer R extends TSchema[]]
? L extends TNever
? Acc
: TFilterNever<R, [...Acc, L]>
: Acc
)
// prettier-ignore
function FilterNever<T extends TSchema[]>(T: [...T]): TFilterNever<T> {
return T.filter(L => !IsNever(L)) as never
}
// ------------------------------------------------------------------
// CompositeProperty
// ------------------------------------------------------------------
// prettier-ignore
type TCompositeProperty<T extends TSchema[], K extends PropertyKey, Acc extends TSchema[] = []> = (
T extends [infer L extends TSchema, ...infer R extends TSchema[]]
? TCompositeProperty<R, K, TFilterNever<[...Acc, ...TIndexFromPropertyKeys<L, [K]>]>>
: Acc
)
// prettier-ignore
function CompositeProperty<T extends TSchema[], K extends PropertyKey>(T: [...T], K: K): TCompositeProperty<T, K> {
return T.reduce((Acc, L) => {
return FilterNever([...Acc, ...IndexFromPropertyKeys(L, [K])])
}, []) as never
}
// ------------------------------------------------------------------
// CompositeProperties
// ------------------------------------------------------------------
// prettier-ignore
type TCompositeProperties<T extends TSchema[], K extends PropertyKey[], Acc = {}> = (
K extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]]
? TCompositeProperties<T, R, Acc & { [_ in L]: TIntersectEvaluated<TCompositeProperty<T, L>> }>
: Acc
)
// prettier-ignore
function CompositeProperties<T extends TSchema[], K extends PropertyKey[] = []>(T: [...T], K: [...K]): TCompositeProperties<T, K> {
return K.reduce((Acc, L) => {
return { ...Acc, [L]: IntersectEvaluated(CompositeProperty(T, L)) }
}, {}) as never
}
// ------------------------------------------------------------------
// Composite
// ------------------------------------------------------------------
// prettier-ignore
type TCompositeEvaluate<
T extends TSchema[],
K extends PropertyKey[] = TCompositeKeys<T>,
P extends TProperties = Evaluate<TCompositeProperties<T, K>>,
R extends TObject = TObject<P>
> = R
// prettier-ignore
export type TComposite<T extends TSchema[]> = TCompositeEvaluate<T>
// prettier-ignore
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)
return R as TComposite<T>
}

View File

@@ -56,10 +56,10 @@ type IncrementStep<T extends string> = T extends IncrementBase['m']
: `${IncrementTake<L>}${R}`
: never
type IncrementReverse<T extends string> = T extends `${infer L}${infer R}` ? `${IncrementReverse<R>}${L}` : T
export type Increment<T extends string> = IncrementReverse<IncrementStep<IncrementReverse<T>>>
export type TIncrement<T extends string> = IncrementReverse<IncrementStep<IncrementReverse<T>>>
/** Increments the given string value + 1 */
export function Increment<T extends string>(T: T): Increment<T> {
return (parseInt(T) + 1).toString() as Increment<T>
export function Increment<T extends string>(T: T): TIncrement<T> {
return (parseInt(T) + 1).toString() as TIncrement<T>
}
// ------------------------------------------------------------------
// Helper: Type Asserts

View File

@@ -27,7 +27,7 @@ THE SOFTWARE.
---------------------------------------------------------------------------*/
import type { TSchema } from '../schema/index'
import { type ZeroString, type UnionToTuple, Increment } from '../helpers/index'
import { type ZeroString, type UnionToTuple, type TIncrement } from '../helpers/index'
import type { TRecursive } from '../recursive/index'
import type { TIntersect } from '../intersect/index'
import type { TUnion } from '../union/index'
@@ -59,31 +59,31 @@ function FromRest<T extends TSchema[]>(T: [...T]): TFromRest<T> {
// FromIntersect
// ------------------------------------------------------------------
// prettier-ignore
type FromIntersect<
type TFromIntersect<
T extends TSchema[],
C extends PropertyKey[][] = TFromRest<T>,
R extends PropertyKey[] = TSetUnionMany<C>
> = R
// prettier-ignore
function FromIntersect<T extends TSchema[]>(T: [...T]): FromIntersect<T> {
function FromIntersect<T extends TSchema[]>(T: [...T]): TFromIntersect<T> {
const C = FromRest(T) as PropertyKey[][]
const R = SetUnionMany(C)
return R as FromIntersect<T>
return R as TFromIntersect<T>
}
// ------------------------------------------------------------------
// FromUnion
// ------------------------------------------------------------------
// prettier-ignore
type FromUnion<
type TFromUnion<
T extends TSchema[],
C extends PropertyKey[][] = TFromRest<T>,
R extends PropertyKey[] = TSetIntersectMany<C>
> = R
// prettier-ignore
function FromUnion<T extends TSchema[]>(T: [...T]): FromUnion<T> {
function FromUnion<T extends TSchema[]>(T: [...T]): TFromUnion<T> {
const C = FromRest(T) as PropertyKey[][]
const R = SetIntersectMany(C)
return R as FromUnion<T>
return R as TFromUnion<T>
}
// ------------------------------------------------------------------
// FromTuple
@@ -91,7 +91,7 @@ function FromUnion<T extends TSchema[]>(T: [...T]): FromUnion<T> {
// prettier-ignore
type TFromTuple<T extends TSchema[], I extends string = ZeroString, Acc extends PropertyKey[] = []> =
T extends [infer _ extends TSchema, ...infer R extends TSchema[]]
? TFromTuple<R, Increment<I>, [...Acc, I]>
? TFromTuple<R, TIncrement<I>, [...Acc, I]>
: Acc
// prettier-ignore
function FromTuple<T extends TSchema[]>(T: [...T]): TFromTuple<T> {
@@ -142,8 +142,8 @@ function FromPatternProperties(patternProperties: Record<PropertyKey, TSchema>):
// prettier-ignore
export type TKeyOfPropertyKeys<T extends TSchema> = (
T extends TRecursive<infer S> ? TKeyOfPropertyKeys<S> :
T extends TIntersect<infer S> ? FromIntersect<S> :
T extends TUnion<infer S> ? FromUnion<S> :
T extends TIntersect<infer S> ? TFromIntersect<S> :
T extends TUnion<infer S> ? TFromUnion<S> :
T extends TTuple<infer S> ? TFromTuple<S> :
T extends TArray<infer S> ? TFromArray<S> :
T extends TObject<infer S> ? TFromProperties<S> :

View File

@@ -37,7 +37,7 @@ export type TSetIncludes<T extends PropertyKey[], S extends PropertyKey> = (
: TSetIncludes<R, S>
: false
)
/** Returns true if element S is in the set of T */
/** Returns true if element right is in the set of left */
// prettier-ignore
export function SetIncludes<T extends PropertyKey[], S extends PropertyKey>(T: [...T], S: S): TSetIncludes<T, S> {
return T.includes(S) as TSetIncludes<T, S>
@@ -46,16 +46,16 @@ export function SetIncludes<T extends PropertyKey[], S extends PropertyKey>(T: [
// SetIsSubset
// ------------------------------------------------------------------
// prettier-ignore
export type SetIsSubset<T extends PropertyKey[], S extends PropertyKey[]> = (
export type TSetIsSubset<T extends PropertyKey[], S extends PropertyKey[]> = (
T extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]]
? TSetIncludes<S, L> extends true
? SetIsSubset<R, S>
? TSetIsSubset<R, S>
: false
: true
)
/** Returns true if T is a subset of S */
export function SetIsSubset<T extends PropertyKey[], S extends PropertyKey[]>(T: [...T], S: [...S]): SetIsSubset<T, S> {
return T.every((L) => SetIncludes(S, L)) as SetIsSubset<T, S>
/** Returns true if left is a subset of right */
export function SetIsSubset<T extends PropertyKey[], S extends PropertyKey[]>(T: [...T], S: [...S]): TSetIsSubset<T, S> {
return T.every((L) => SetIncludes(S, L)) as TSetIsSubset<T, S>
}
// ------------------------------------------------------------------
// SetDistinct
@@ -78,7 +78,7 @@ export function SetDistinct<T extends PropertyKey[]>(T: [...T]): TSetDistinct<T>
export type TSetIntersect<T extends PropertyKey[], S extends PropertyKey[], Acc extends PropertyKey[] = []> = (
T extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]]
? TSetIncludes<S, L> extends true
? TSetIntersect<R, S, [L, ...Acc]>
? TSetIntersect<R, S, [...Acc, L]>
: TSetIntersect<R, S, [...Acc]>
: Acc
)
@@ -90,14 +90,12 @@ export function SetIntersect<T extends PropertyKey[], S extends PropertyKey[]>(T
// SetUnion
// ------------------------------------------------------------------
// prettier-ignore
export type TSetUnion<T extends PropertyKey[], S extends PropertyKey[], Acc extends PropertyKey[] = S> = (
T extends [infer L extends PropertyKey, ...infer R extends PropertyKey[]]
? TSetUnion<R, S, [...Acc, L]>
: Acc
export type TSetUnion<T extends PropertyKey[], S extends PropertyKey[]> = (
[...T, ...S]
)
/** Returns the Union of the given sets */
export function SetUnion<T extends PropertyKey[], S extends PropertyKey[]>(T: [...T], S: [...S]): TSetUnion<T, S> {
return [...T, ...S] as never
return [...T, ...S]
}
// ------------------------------------------------------------------
// SetComplement
@@ -119,17 +117,35 @@ export function SetComplement<T extends PropertyKey[], S extends PropertyKey[]>(
// SetIntersectMany
// ------------------------------------------------------------------
// prettier-ignore
export type TSetIntersectMany<T extends PropertyKey[][], Acc extends PropertyKey[] = []> = (
T extends [infer L extends PropertyKey[]] ? L :
type TSetIntersectManyResolve<T extends PropertyKey[][], Acc extends PropertyKey[]> = (
T extends [infer L extends PropertyKey[], ...infer R extends PropertyKey[][]]
? TSetIntersectMany<R, TSetIntersect<Acc, L>>
? TSetIntersectManyResolve<R, TSetIntersect<Acc, L>>
: Acc
)
/** Returns the Intersect of multiple sets */
// prettier-ignore
function SetIntersectManyResolve<T extends PropertyKey[][], Acc extends PropertyKey[]>(T: [...T], Init: Acc): TSetIntersectManyResolve<T, Acc> {
return T.reduce((Acc: PropertyKey[], L: PropertyKey[]) => {
return SetIntersect(Acc, L)
}, Init) as never
}
// prettier-ignore
export type TSetIntersectMany<T extends PropertyKey[][]> = (
T extends [infer L extends PropertyKey[]]
? L
// Use left to initialize the accumulator for resolve
: T extends [infer L extends PropertyKey[], ...infer R extends PropertyKey[][]]
? TSetIntersectManyResolve<R, L>
: []
)
// prettier-ignore
export function SetIntersectMany<T extends PropertyKey[][]>(T: [...T]): TSetIntersectMany<T> {
return (
T.length === 1 ? T[0] : T.reduce((Acc, L) => [...SetIntersect(Acc, L)], [])
T.length === 1
? T[0]
// Use left to initialize the accumulator for resolve
: T.length > 1
? SetIntersectManyResolve(T.slice(1), T[0])
: []
) as TSetIntersectMany<T>
}
// ------------------------------------------------------------------

View File

@@ -130,8 +130,8 @@ export class JsonTypeBuilder {
return Capitalize(schema, options)
}
/** `[Json]` Creates a Composite object type */
public Composite<T extends TObject[]>(objects: [...T], options?: ObjectOptions): TComposite<T> {
return Composite(objects, options) as any // (error) TS 5.4.0-dev - review TComposite implementation
public Composite<T extends TSchema[]>(schemas: [...T], options?: ObjectOptions): TComposite<T> {
return Composite(schemas, options) // (error) TS 5.4.0-dev - review TComposite implementation
}
/** `[JavaScript]` Creates a readonly const type from the given value. */
public Const</* const (not supported in 4.0) */ T>(value: T, options: SchemaOptions = {}): TConst<T> {

View File

@@ -91,4 +91,21 @@ describe('compiler-ajv/Composite', () => {
const B = Type.Composite([T])
Assert.IsEqual(A, B)
})
// prettier-ignore
it('Should composite intersection', () => {
const T = Type.Composite([
Type.Intersect([
Type.Object({ x: Type.Number() })
]),
Type.Intersect([
Type.Object({ y: Type.Number() })
]),
Type.Intersect([
Type.Object({ z: Type.Number() })
]),
])
Ok(T, { x: 1, y: 2, z: 3 })
Fail(T, { x: 1, y: 2, z: '3' })
Fail(T, { x: 1, y: 2 })
})
})

View File

@@ -81,4 +81,21 @@ describe('compiler/Composite', () => {
Fail(T, { x: { x: '1' }, y: { x: '' } })
Fail(T, { x: { x: 1 }, y: { x: 1 } })
})
// prettier-ignore
it('Should composite intersection', () => {
const T = Type.Composite([
Type.Intersect([
Type.Object({ x: Type.Number() })
]),
Type.Intersect([
Type.Object({ y: Type.Number() })
]),
Type.Intersect([
Type.Object({ z: Type.Number() })
]),
])
Ok(T, { x: 1, y: 2, z: 3 })
Fail(T, { x: 1, y: 2, z: '3' })
Fail(T, { x: 1, y: 2 })
})
})

View File

@@ -36,8 +36,8 @@ describe('type/guard/TComposite', () => {
Assert.IsTrue(TypeGuard.IsOptional(T.properties.x))
Assert.IsEqual(T.required, undefined)
})
// prettier-ignore
it('Should produce required property if some composited properties are not optional', () => {
// prettier-ignore
const T = Type.Composite([
Type.Object({ x: Type.Optional(Type.Number()) }),
Type.Object({ x: Type.Number() })
@@ -45,12 +45,119 @@ describe('type/guard/TComposite', () => {
Assert.IsFalse(TypeGuard.IsOptional(T.properties.x))
Assert.IsTrue(T.required!.includes('x'))
})
// prettier-ignore
it('Should preserve single optional property', () => {
// prettier-ignore
const T = Type.Composite([
Type.Object({ x: Type.Optional(Type.Number()) }),
])
Assert.IsTrue(TypeGuard.IsOptional(T.properties.x))
Assert.IsEqual(T.required, undefined)
})
// ----------------------------------------------------------------
// Intersect
// ----------------------------------------------------------------
// prettier-ignore
it('Should composite Intersect 1', () => {
const T = Type.Composite([
Type.Intersect([
Type.Object({ x: Type.Number() }),
Type.Object({ y: Type.Number() }),
]),
Type.Intersect([
Type.Object({ z: Type.Number() }),
])
])
Assert.IsEqual(T, Type.Object({
x: Type.Number(),
y: Type.Number(),
z: Type.Number()
}))
})
// prettier-ignore
it('Should composite Intersect 2', () => {
const T = Type.Composite([
Type.Intersect([
Type.Object({ x: Type.Number() }),
Type.Object({ x: Type.Number() }),
]),
Type.Intersect([
Type.Object({ x: Type.Number() }),
])
])
Assert.IsEqual(T, Type.Object({
x: Type.Intersect([Type.Intersect([Type.Number(), Type.Number()]), Type.Number()])
}))
})
// prettier-ignore
it('Should composite Intersect 3', () => {
const T = Type.Composite([
Type.Number(),
Type.Boolean()
])
Assert.IsEqual(T, Type.Object({}))
})
// prettier-ignore
it('Should composite Intersect 4', () => {
const T = Type.Composite([
Type.Number(),
Type.Boolean(),
Type.Object({ x: Type.String() })
])
Assert.IsEqual(T, Type.Object({
x: Type.String()
}))
})
// prettier-ignore
it('Should composite Intersect 5', () => {
const T = Type.Composite([
Type.Object({ x: Type.Optional(Type.String()) }),
Type.Object({ x: Type.String() })
])
Assert.IsEqual(T, Type.Object({
x: Type.Intersect([Type.String(), Type.String()])
}))
})
// prettier-ignore
it('Should composite Intersect 6', () => {
const T = Type.Composite([
Type.Object({ x: Type.Optional(Type.String()) }),
Type.Object({ x: Type.Optional(Type.String()) })
])
Assert.IsEqual(T, Type.Object({
x: Type.Optional(Type.Intersect([Type.String(), Type.String()]))
}))
})
// ----------------------------------------------------------------
// Union
// ----------------------------------------------------------------
// prettier-ignore
it('Should composite Union 1', () => {
const T = Type.Composite([
Type.Union([
Type.Object({ x: Type.Number() }),
Type.Object({ y: Type.Number() }),
]),
Type.Union([
Type.Object({ z: Type.Number() }),
])
])
Assert.IsEqual(T, Type.Object({
z: Type.Intersect([Type.Union([Type.Never(), Type.Never()]), Type.Number()])
}))
})
// prettier-ignore
it('Should composite Union 2', () => {
const T = Type.Composite([
Type.Union([
Type.Object({ x: Type.Number() }),
Type.Object({ x: Type.Number() }),
]),
Type.Union([
Type.Object({ x: Type.Number() }),
])
])
Assert.IsEqual(T, Type.Object({
x: Type.Intersect([Type.Union([Type.Number(), Type.Number()]), Type.Number()])
}))
})
})

View File

@@ -4,5 +4,6 @@ import './guard/index'
import './intrinsic/index'
import './normalize/index'
import './registry/index'
import './sets/index'
import './template-literal/index'
import './value/index'

View File

@@ -0,0 +1 @@
import './sets'

View File

@@ -0,0 +1,167 @@
import * as Type from '@sinclair/typebox'
import { Assert } from '../../assert'
describe('type/sets', () => {
// ----------------------------------------------------------------
// Distinct
// ----------------------------------------------------------------
it('Should Distinct', () => {
const R = Type.SetDistinct([1, 1, 2, 2])
Assert.IsEqual(R, [1, 2])
})
// ----------------------------------------------------------------
// Includes
// ----------------------------------------------------------------
it('Should Includes 1', () => {
const R = Type.SetIncludes([1, 2, 3, 4], 1)
Assert.IsTrue(R)
})
it('Should Includes 2', () => {
const R = Type.SetIncludes([1, 2, 3, 4], 7)
Assert.IsFalse(R)
})
// ----------------------------------------------------------------
// IsSubset
// ----------------------------------------------------------------
it('Should IsSubset 1', () => {
const R = Type.SetIsSubset([1, 2], [1, 2])
Assert.IsTrue(R)
})
it('Should IsSubset 2', () => {
const R = Type.SetIsSubset([1, 2], [1, 2, 3])
Assert.IsTrue(R)
})
it('Should IsSubset 3', () => {
const R = Type.SetIsSubset([1, 2], [1])
Assert.IsFalse(R)
})
// ----------------------------------------------------------------
// Intersect
// ----------------------------------------------------------------
it('Should Intersect 1', () => {
const R = Type.SetIntersect([1, 2], [1, 2])
Assert.IsEqual(R, [1, 2])
})
it('Should Intersect 2', () => {
const R = Type.SetIntersect([1], [1, 2])
Assert.IsEqual(R, [1])
})
it('Should Intersect 3', () => {
const R = Type.SetIntersect([1, 2], [1])
Assert.IsEqual(R, [1])
})
it('Should Intersect 4', () => {
const R = Type.SetIntersect([], [1])
Assert.IsEqual(R, [])
})
it('Should Intersect 5', () => {
const R = Type.SetIntersect([1], [])
Assert.IsEqual(R, [])
})
it('Should Intersect 6', () => {
const R = Type.SetIntersect([1], [2])
Assert.IsEqual(R, [])
})
// ----------------------------------------------------------------
// Union
// ----------------------------------------------------------------
it('Should Union 1', () => {
const R = Type.SetUnion([1, 2], [1, 2])
Assert.IsEqual(R, [1, 2, 1, 2])
})
it('Should Union 2', () => {
const R = Type.SetUnion([1], [1, 2])
Assert.IsEqual(R, [1, 1, 2])
})
it('Should Union 3', () => {
const R = Type.SetUnion([1, 2], [1])
Assert.IsEqual(R, [1, 2, 1])
})
it('Should Union 4', () => {
const R = Type.SetUnion([], [1])
Assert.IsEqual(R, [1])
})
it('Should Union 5', () => {
const R = Type.SetUnion([1], [])
Assert.IsEqual(R, [1])
})
// ----------------------------------------------------------------
// Complement
// ----------------------------------------------------------------
it('Should Complement 1', () => {
const R = Type.SetComplement([1, 2, 3, 4], [2, 3])
Assert.IsEqual(R, [1, 4])
})
it('Should Complement 2', () => {
const R = Type.SetComplement([2, 3], [1, 2, 3, 4])
Assert.IsEqual(R, [])
})
// ----------------------------------------------------------------
// IntersectMany
// ----------------------------------------------------------------
it('Should IntersectMany 1', () => {
const R = Type.SetIntersectMany([[1, 2, 3], [1, 2], [1]] as const)
Assert.IsEqual(R, [1])
})
it('Should IntersectMany 2', () => {
const R = Type.SetIntersectMany([[1], [1, 2], [1, 2, 3]] as const)
Assert.IsEqual(R, [1])
})
it('Should IntersectMany 3', () => {
const R = Type.SetIntersectMany([
[1, 2],
[1, 2],
] as const)
Assert.IsEqual(R, [1, 2])
})
it('Should IntersectMany 4', () => {
const R = Type.SetIntersectMany([[1], [2]] as const)
Assert.IsEqual(R, [])
})
it('Should IntersectMany 5', () => {
const R = Type.SetIntersectMany([[1], []] as const)
Assert.IsEqual(R, [])
})
it('Should IntersectMany 6', () => {
const R = Type.SetIntersectMany([[], [1]] as const)
Assert.IsEqual(R, [])
})
it('Should IntersectMany 7', () => {
const R = Type.SetIntersectMany([[1], [1], [1], [1], []] as const)
Assert.IsEqual(R, [])
})
it('Should IntersectMany 8', () => {
const R = Type.SetIntersectMany([[], [1], [1], [1], [1]] as const)
Assert.IsEqual(R, [])
})
// ----------------------------------------------------------------
// UnionMany
// ----------------------------------------------------------------
it('Should UnionMany 1', () => {
const R = Type.SetUnionMany([[1, 2, 3], [1, 2], [1]] as const)
Assert.IsEqual(R, [1, 2, 3, 1, 2, 1])
})
it('Should UnionMany 2', () => {
const R = Type.SetUnionMany([[1], [1, 2], [1, 2, 3]] as const)
Assert.IsEqual(R, [1, 1, 2, 1, 2, 3])
})
it('Should UnionMany 3', () => {
const R = Type.SetUnionMany([
[1, 2],
[1, 2],
] as const)
Assert.IsEqual(R, [1, 2, 1, 2])
})
it('Should UnionMany 4', () => {
const R = Type.SetUnionMany([[1], [2]] as const)
Assert.IsEqual(R, [1, 2])
})
it('Should UnionMany 5', () => {
const R = Type.SetUnionMany([[1], []] as const)
Assert.IsEqual(R, [1])
})
it('Should UnionMany 6', () => {
const R = Type.SetUnionMany([[], [1]] as const)
Assert.IsEqual(R, [1])
})
})

View File

@@ -1,5 +1,5 @@
import { Expect } from './assert'
import { Type, TObject, TIntersect, TNumber, TBoolean } from '@sinclair/typebox'
import { Type, TOptional, TObject, TIntersect, TNumber, TBoolean } from '@sinclair/typebox'
// ----------------------------------------------------------------------------
// Overlapping - Non Varying
@@ -127,3 +127,60 @@ import { Type, TObject, TIntersect, TNumber, TBoolean } from '@sinclair/typebox'
Type.Object({ x: Type.Boolean() })
])
}
// ------------------------------------------------------------------
// Intersect
// ------------------------------------------------------------------
// prettier-ignore
{
const T: TObject<{
x: TNumber;
y: TNumber;
z: TNumber;
}> = Type.Composite([
Type.Intersect([
Type.Object({ x: Type.Number() }),
Type.Object({ y: Type.Number() }),
]),
Type.Intersect([
Type.Object({ z: Type.Number() })
])
])
}
// prettier-ignore
{
const T: TObject<{
x: TIntersect<[TNumber, TNumber]>;
y: TIntersect<[TNumber, TNumber]>;
}> = Type.Composite([
Type.Intersect([
Type.Object({ x: Type.Number() }),
Type.Object({ y: Type.Number() }),
]),
Type.Intersect([
Type.Object({ x: Type.Number() }),
Type.Object({ y: Type.Number() }),
])
])
}
// prettier-ignore
{
const T: TObject<{
x: TIntersect<[TNumber, TNumber]>;
}> = Type.Composite([
Type.Intersect([
Type.Object({ x: Type.Optional(Type.Number()) }),
Type.Object({ x: Type.Number() }),
])
])
}
// prettier-ignore
{
const T: TObject<{
x: TOptional<TIntersect<[TNumber, TNumber]>>;
}> = Type.Composite([
Type.Intersect([
Type.Object({ x: Type.Optional(Type.Number()) }),
Type.Object({ x: Type.Optional(Type.Number()) }),
])
])
}