@@ -31,83 +31,75 @@ THE SOFTWARE.
3131
3232import { Guard } from '../../../guard/index.ts'
3333import { type TSchema , IsSchema } from '../../types/schema.ts'
34- import { type TIntersect , IsIntersect } from '../../types/intersect.ts'
3534import { type TUnion , IsUnion } from '../../types/union.ts'
3635import { type TObject , IsObject } from '../../types/object.ts'
3736import { type TTuple , IsTuple } from '../../types/tuple.ts'
3837import { type TComposite , Composite } from './composite.ts'
3938import { type TNarrow , Narrow } from './narrow.ts'
40- import { type TEvaluateType , EvaluateType } from './evaluate.ts'
4139
40+ import { type TEvaluateType , EvaluateType } from './evaluate.ts'
4241import { type TEvaluateIntersect , EvaluateIntersect } from './evaluate.ts'
4342
44- // -----------------------------------------------------------------------------------------
45- // CanDistribute
46- // -----------------------------------------------------------------------------------------
47- type TCanDistribute < Type extends TSchema >
43+ // ------------------------------------------------------------------
44+ // IsObjectLike
45+ // ------------------------------------------------------------------
46+ type TIsObjectLike < Type extends TSchema >
4847 = Type extends TObject | TTuple ? true : false
49- function CanDistribute < Type extends TSchema > ( type : Type ) {
48+ function IsObjectLike < Type extends TSchema > ( type : Type ) {
5049 return IsObject ( type ) || IsTuple ( type )
5150}
52- // -----------------------------------------------------------------------------------------
53- //
54- // DistributeNormalize
55- //
56- // Distribute operands may be intersections, this function forces a conditional
57- // sub-evaluation of an intersection to give either a TTObject | scalar. This
58- // preflight mapping is used prior to running the logic for the DistributeOperation.
59- //
60- // -----------------------------------------------------------------------------------------
61- type TDistributeNormalize < Type extends TSchema > =
62- Type extends TIntersect < infer Types extends TSchema [ ] >
63- ? TEvaluateIntersect < Types >
64- : Type
65-
66- function DistributeNormalize < Type extends TSchema > ( type : Type ) : TDistributeNormalize < Type > {
51+ // ------------------------------------------------------------------
52+ // IsUnionOperand
53+ // ------------------------------------------------------------------
54+ type TIsUnionOperand < Left extends TSchema , Right extends TSchema ,
55+ IsUnionLeft extends boolean = Left extends TUnion ? true : false ,
56+ IsUnionRight extends boolean = Right extends TUnion ? true : false ,
57+ Result extends boolean = (
58+ [ IsUnionLeft , IsUnionRight ] extends [ true , true ] ? true :
59+ [ IsUnionLeft , IsUnionRight ] extends [ false , true ] ? true :
60+ [ IsUnionLeft , IsUnionRight ] extends [ true , false ] ? true :
61+ false
62+ )
63+ > = Result
64+ function IsUnionOperand < Left extends TSchema , Right extends TSchema > ( left : Left , right : Right ) : TIsUnionOperand < Left , Right > {
65+ const isUnionLeft = IsUnion ( left )
66+ const isUnionRight = IsUnion ( right )
6767 return (
68- IsIntersect ( type )
69- ? EvaluateIntersect ( type . allOf )
70- : type
68+ isUnionLeft && isUnionRight ? true :
69+ ! isUnionLeft && isUnionRight ? true :
70+ isUnionLeft && ! isUnionRight ? true :
71+ false
7172 ) as never
7273}
7374// -----------------------------------------------------------------------------------------
74- //
7575// DistributeOperation
76- //
77- // This function is crucial for type distribution and evaluation. Unlike TypeScript,
78- // TypeBox does not distribute scalar types (e.g., numbers, strings) against object
79- // types. When an object is encountered, the distribution shifts from scalar to
80- // composite, with no option to revert to scalar distribution.
81- //
82- // This behavior is similar to TypeScript, where a number distributed against an object
83- // is composited with the number's built-in methods. However, in TypeBox, numbers lack
84- // methods and are treated as symbolic types. Discarding them is effectively the same
85- // as compositing an empty `{}`. The empty object distribution for symbolic scalar
86- // types is the rationale behind the following logic.
87- //
8876// -----------------------------------------------------------------------------------------
8977type TDistributeOperation < Left extends TSchema , Right extends TSchema ,
90- NormalLeft extends TSchema = TDistributeNormalize < Left > ,
91- NormalRight extends TSchema = TDistributeNormalize < Right > ,
92- IsObjectLeft extends boolean = TCanDistribute < NormalLeft > ,
93- IsObjectRight extends boolean = TCanDistribute < NormalRight > ,
78+ EvaluatedLeft extends TSchema = TEvaluateType < Left > ,
79+ EvaluatedRight extends TSchema = TEvaluateType < Right > ,
80+ IsUnionOperand extends boolean = TIsUnionOperand < EvaluatedLeft , EvaluatedRight > ,
81+ IsObjectLeft extends boolean = TIsObjectLike < EvaluatedLeft > ,
82+ IsObjectRight extends boolean = TIsObjectLike < EvaluatedRight > ,
9483 Result extends TSchema = (
95- [ IsObjectLeft , IsObjectRight ] extends [ true , true ] ? TComposite < TEvaluateType < NormalLeft > , NormalRight > :
96- [ IsObjectLeft , IsObjectRight ] extends [ true , false ] ? TEvaluateType < NormalLeft > :
97- [ IsObjectLeft , IsObjectRight ] extends [ false , true ] ? NormalRight :
98- TNarrow < TEvaluateType < NormalLeft > , NormalRight >
84+ [ IsUnionOperand ] extends [ true ] ? TEvaluateIntersect < [ EvaluatedLeft , EvaluatedRight ] > :
85+ [ IsObjectLeft , IsObjectRight ] extends [ true , true ] ? TComposite < EvaluatedLeft , EvaluatedRight > :
86+ [ IsObjectLeft , IsObjectRight ] extends [ true , false ] ? EvaluatedLeft :
87+ [ IsObjectLeft , IsObjectRight ] extends [ false , true ] ? EvaluatedRight :
88+ TNarrow < EvaluatedLeft , EvaluatedRight >
9989 )
10090> = Result
10191function DistributeOperation < Left extends TSchema , Right extends TSchema > ( left : Left , right : Right ) : TDistributeOperation < Left , Right > {
102- const normalLeft = DistributeNormalize ( left )
103- const normalRight = DistributeNormalize ( right )
104- const isObjectLeft = CanDistribute ( normalLeft )
105- const IsObjectRight = CanDistribute ( normalRight )
92+ const evaluatedLeft = EvaluateType ( left )
93+ const evaluatedRight = EvaluateType ( right )
94+ const isUnionOperand = IsUnionOperand ( evaluatedLeft , evaluatedRight )
95+ const isObjectLeft = IsObjectLike ( evaluatedLeft )
96+ const IsObjectRight = IsObjectLike ( evaluatedRight )
10697 const result = (
107- isObjectLeft && IsObjectRight ? Composite ( EvaluateType ( normalLeft ) , normalRight ) :
108- isObjectLeft && ! IsObjectRight ? EvaluateType ( normalLeft ) :
109- ! isObjectLeft && IsObjectRight ? normalRight :
110- Narrow ( EvaluateType ( normalLeft ) , normalRight )
98+ isUnionOperand ? EvaluateIntersect ( [ evaluatedLeft , evaluatedRight ] ) :
99+ isObjectLeft && IsObjectRight ? Composite ( evaluatedLeft , evaluatedRight ) :
100+ isObjectLeft && ! IsObjectRight ? evaluatedLeft :
101+ ! isObjectLeft && IsObjectRight ? evaluatedRight :
102+ Narrow ( evaluatedLeft , evaluatedRight )
111103 )
112104 return result as never
113105}
0 commit comments