@@ -168,21 +168,14 @@ export type TInstanceType<T extends TConstructor<TSchema[], TSchema>> = T['retur
168168// --------------------------------------------------------------------------
169169// TComposite
170170// --------------------------------------------------------------------------
171- export type TCompositeUnion < Left extends TSchema , Right extends TSchema > = Ensure < TUnion < [ Left , Right ] > >
172- // note: we need to take the left and right as the accumulator is assigned for multiple composite property sets with missing properties.
173- export type TCompositeUnionLeft < T extends TObject , Acc extends TProperties > = {
174- [ K in keyof T [ 'properties' ] ] : K extends keyof Acc ? TCompositeUnion < T [ 'properties' ] [ K ] , Acc [ K ] > : T [ 'properties' ] [ K ]
171+ export type TCompositeEvaluateArray < T extends readonly TSchema [ ] , P extends unknown [ ] > = { [ K in keyof T ] : T [ K ] extends TSchema ? Static < T [ K ] , P > : never }
172+ export type TCompositeArray < T extends readonly TObject [ ] > = { [ K in keyof T ] : T [ K ] extends TObject < infer P > ? P : { } }
173+ export type TCompositeProperties < I extends unknown , T extends readonly any [ ] > = Evaluate < T extends [ infer A , ...infer B ] ? TCompositeProperties < I & A , B > : I extends object ? I : { } >
174+ export interface TComposite < T extends TObject [ ] = TObject [ ] > extends TObject {
175+ [ Hint ] : 'Composite' // Hint is required to differentiate between object | intersection on pick, omit, required, partial and keyof
176+ static : Evaluate < TCompositeProperties < unknown , TCompositeEvaluateArray < T , this[ 'params' ] > > >
177+ properties : TCompositeProperties < unknown , TCompositeArray < T > >
175178}
176- export type TCompositeUnionRight < T extends TObject , Acc extends TProperties > = {
177- [ K in keyof Acc ] : K extends keyof T [ 'properties' ] ? TCompositeUnion < T [ 'properties' ] [ K ] , Acc [ K ] > : Acc [ K ]
178- }
179- export type TCompositeUnionObject < T extends TObject , Acc extends TProperties > = Evaluate < TCompositeUnionLeft < T , Acc > & TCompositeUnionRight < T , Acc > >
180- // prettier-ignore
181- export type TCompositeProperties < T extends TObject [ ] , Acc extends TProperties > =
182- T extends [ ...infer L , infer R ] ? TCompositeProperties < Assert < L , TObject [ ] > , TCompositeUnionObject < Assert < R , TObject > , Acc > > :
183- T extends [ ] ? Acc :
184- never
185- export type TComposite < T extends TObject [ ] = TObject [ ] > = Ensure < TObject < TCompositeProperties < T , { } > > >
186179// --------------------------------------------------------------------------
187180// TConstructor
188181// --------------------------------------------------------------------------
@@ -275,6 +268,7 @@ export interface IntersectOptions extends SchemaOptions {
275268 unevaluatedProperties ?: TUnevaluatedProperties
276269}
277270export type TIntersectStatic < T extends TSchema [ ] , P extends unknown [ ] > = TupleToIntersect < { [ K in keyof T ] : Static < Assert < T [ K ] , TSchema > , P > } >
271+
278272export interface TIntersect < T extends TSchema [ ] = TSchema [ ] > extends TSchema , IntersectOptions {
279273 [ Kind ] : 'Intersect'
280274 type ?: 'object'
@@ -292,6 +286,7 @@ export type TKeyOfTuple<T extends TSchema> = {
292286 : never
293287// prettier-ignore
294288export type TKeyOf < T extends TSchema = TSchema > = (
289+ T extends TComposite ? TKeyOfTuple < T > :
295290 T extends TIntersect ? TKeyOfTuple < T > :
296291 T extends TUnion ? TKeyOfTuple < T > :
297292 T extends TObject ? TKeyOfTuple < T > :
@@ -312,7 +307,7 @@ export interface TLiteral<T extends TLiteralValue = TLiteralValue> extends TSche
312307export interface TNever extends TSchema {
313308 [ Kind ] : 'Never'
314309 static : never
315- allOf : [ { type : 'boolean' ; const : false } , { type : 'boolean' ; const : true } ]
310+ not : { }
316311}
317312// --------------------------------------------------------------------------
318313// TNot
@@ -381,6 +376,7 @@ export type TOmitArray<T extends TSchema[], K extends keyof any> = Assert<{ [K2
381376export type TOmitProperties < T extends TProperties , K extends keyof any > = Evaluate < Assert < Omit < T , K > , TProperties > >
382377// prettier-ignore
383378export type TOmit < T extends TSchema , K extends keyof any > =
379+ T extends TComposite < infer S > ? TComposite < TOmitArray < S , K > > :
384380 T extends TIntersect < infer S > ? TIntersect < TOmitArray < S , K > > :
385381 T extends TUnion < infer S > ? TUnion < TOmitArray < S , K > > :
386382 T extends TObject < infer S > ? TObject < TOmitProperties < S , K > > :
@@ -392,6 +388,7 @@ export type TParameters<T extends TFunction> = TTuple<T['parameters']>
392388// --------------------------------------------------------------------------
393389// TPartial
394390// --------------------------------------------------------------------------
391+ export type TPartialObjectArray < T extends TObject [ ] > = Assert < { [ K in keyof T ] : TPartial < Assert < T [ K ] , TObject > > } , TObject [ ] >
395392export type TPartialArray < T extends TSchema [ ] > = Assert < { [ K in keyof T ] : TPartial < Assert < T [ K ] , TSchema > > } , TSchema [ ] >
396393// prettier-ignore
397394export type TPartialProperties < T extends TProperties > = Evaluate < Assert < {
@@ -402,7 +399,8 @@ export type TPartialProperties<T extends TProperties> = Evaluate<Assert<{
402399 TOptional < T [ K ] >
403400} , TProperties > >
404401// prettier-ignore
405- export type TPartial < T extends TSchema > =
402+ export type TPartial < T extends TSchema > =
403+ T extends TComposite < infer S > ? TComposite < TPartialArray < S > > :
406404 T extends TIntersect < infer S > ? TIntersect < TPartialArray < S > > :
407405 T extends TUnion < infer S > ? TUnion < TPartialArray < S > > :
408406 T extends TObject < infer S > ? TObject < TPartialProperties < S > > :
@@ -420,11 +418,11 @@ export type TPickProperties<T extends TProperties, K extends keyof any> =
420418 } ) : never
421419// prettier-ignore
422420export type TPick < T extends TSchema , K extends keyof any > =
421+ T extends TComposite < infer S > ? TComposite < TPickArray < S , K > > :
423422 T extends TIntersect < infer S > ? TIntersect < TPickArray < S , K > > :
424423 T extends TUnion < infer S > ? TUnion < TPickArray < S , K > > :
425424 T extends TObject < infer S > ? TObject < TPickProperties < S , K > > :
426425 T
427-
428426// --------------------------------------------------------------------------
429427// TPromise
430428// --------------------------------------------------------------------------
@@ -489,6 +487,7 @@ export type TRequiredProperties<T extends TProperties> = Evaluate<Assert<{
489487} , TProperties > >
490488// prettier-ignore
491489export type TRequired < T extends TSchema > =
490+ T extends TComposite < infer S > ? TComposite < TRequiredArray < S > > :
492491 T extends TIntersect < infer S > ? TIntersect < TRequiredArray < S > > :
493492 T extends TUnion < infer S > ? TUnion < TRequiredArray < S > > :
494493 T extends TObject < infer S > ? TObject < TRequiredProperties < S > > :
@@ -1845,15 +1844,41 @@ export class StandardTypeBuilder extends TypeBuilder {
18451844 public Boolean ( options : SchemaOptions = { } ) : TBoolean {
18461845 return this . Create ( { ...options , [ Kind ] : 'Boolean' , type : 'boolean' } )
18471846 }
1848- /** `[Standard]` Creates a Composite object type that will union any overlapping properties of the given object array */
1849- public Composite < T extends TObject [ ] > ( schemas : [ ...T ] , options ?: ObjectOptions ) : TComposite < T > {
1850- const properties = { } as TProperties
1851- for ( const object of schemas ) {
1852- for ( const [ key , property ] of globalThis . Object . entries ( object . properties ) ) {
1853- properties [ key ] = key in properties ? this . Union ( [ properties [ key ] , property ] ) : TypeClone . Clone ( property , { } )
1847+ /** `[Standard]` Creates a Composite object type. */
1848+ public Composite < T extends TObject [ ] > ( objects : [ ...T ] , options ?: ObjectOptions ) : TComposite < T > {
1849+ const isOptionalAll = ( objects : TObject [ ] , key : string ) => objects . every ( ( object ) => ! ( key in object . properties ) || IsOptional ( object . properties [ key ] ) )
1850+ const IsOptional = ( schema : TSchema ) => TypeGuard . TOptional ( schema ) || TypeGuard . TReadonlyOptional ( schema )
1851+ const [ required , optional ] = [ new Set < string > ( ) , new Set < string > ( ) ]
1852+ for ( const object of objects ) {
1853+ for ( const key of globalThis . Object . getOwnPropertyNames ( object . properties ) ) {
1854+ if ( isOptionalAll ( objects , key ) ) optional . add ( key )
1855+ }
1856+ }
1857+ for ( const object of objects ) {
1858+ for ( const key of globalThis . Object . getOwnPropertyNames ( object . properties ) ) {
1859+ if ( ! optional . has ( key ) ) required . add ( key )
18541860 }
18551861 }
1856- return this . Object ( properties , options ) as TComposite < T >
1862+ const properties = { } as Record < keyof any , any >
1863+ for ( const object of objects ) {
1864+ for ( const [ key , schema ] of Object . entries ( object . properties ) ) {
1865+ const property = TypeClone . Clone ( schema , { } )
1866+ if ( ! optional . has ( key ) ) delete property [ Modifier ]
1867+ if ( key in properties ) {
1868+ const left = TypeExtends . Extends ( properties [ key ] , property ) !== TypeExtendsResult . False
1869+ const right = TypeExtends . Extends ( property , properties [ key ] ) !== TypeExtendsResult . False
1870+ if ( ! left && ! right ) properties [ key ] = Type . Never ( )
1871+ if ( ! left && right ) properties [ key ] = property
1872+ } else {
1873+ properties [ key ] = property
1874+ }
1875+ }
1876+ }
1877+ if ( required . size > 0 ) {
1878+ return this . Create ( { ...options , [ Kind ] : 'Object' , [ Hint ] : 'Composite' , type : 'object' , properties, required : [ ...required ] } )
1879+ } else {
1880+ return this . Create ( { ...options , [ Kind ] : 'Object' , [ Hint ] : 'Composite' , type : 'object' , properties } )
1881+ }
18571882 }
18581883 /** `[Standard]` Creates a Enum type */
18591884 public Enum < T extends Record < string , string | number > > ( item : T , options : SchemaOptions = { } ) : TEnum < T > {
0 commit comments