@@ -12,14 +12,17 @@ import type {
1212 GetEnum ,
1313 GetEnums ,
1414 GetModel ,
15+ GetModelDiscriminator ,
1516 GetModelField ,
1617 GetModelFields ,
1718 GetModelFieldType ,
1819 GetModels ,
20+ GetSubModels ,
1921 GetTypeDefField ,
2022 GetTypeDefFields ,
2123 GetTypeDefFieldType ,
2224 GetTypeDefs ,
25+ IsDelegateModel ,
2326 ModelFieldIsOptional ,
2427 NonRelationFields ,
2528 ProcedureDef ,
@@ -70,27 +73,19 @@ export type DefaultModelResult<
7073 Options extends QueryOptions < Schema > = QueryOptions < Schema > ,
7174 Optional = false ,
7275 Array = false ,
76+ // Guard: if Model is the generic `string` type (which happens when Schema is the base
77+ // SchemaDef interface), skip all delegate expansion. Checking [string] extends [Model]
78+ // is O(1) and short-circuits before any of the more expensive type computations run,
79+ // keeping the total instantiation count within TypeScript's recursion budget.
80+ IsGenericModel = [ string ] extends [ Model ] ? true : false ,
7381> = WrapType <
74- {
75- [ Key in NonRelationFields < Schema , Model > as ShouldOmitField < Schema , Model , Options , Key , Omit > extends true
76- ? never
77- : Key ] : MapModelFieldType < Schema , Model , Key > ;
78- } ,
79- // TODO: revisit how to efficiently implement discriminated sub model types
80- // IsDelegateModel<Schema, Model> extends true
81- // ? // delegate model's selection result is a union of all sub-models
82- // DelegateUnionResult<Schema, Model, Options, GetSubModels<Schema, Model>, Omit>
83- // : {
84- // [Key in NonRelationFields<Schema, Model> as ShouldOmitField<
85- // Schema,
86- // Model,
87- // Options,
88- // Key,
89- // Omit
90- // > extends true
91- // ? never
92- // : Key]: MapModelFieldType<Schema, Model, Key>;
93- // },
82+ IsGenericModel extends true
83+ ? // generic model — return flat type immediately to avoid expensive recursion
84+ FlatModelResult < Schema , Model , Omit , Options >
85+ : IsDelegateModel < Schema , Model > extends true
86+ ? // delegate model's selection result is a union of all sub-models
87+ DelegateUnionResult < Schema , Model , Options , GetSubModels < Schema , Model > , Omit >
88+ : FlatModelResult < Schema , Model , Omit , Options > ,
9489 Optional ,
9590 Array
9691> ;
@@ -135,15 +130,55 @@ type SchemaLevelOmit<
135130 Field extends GetModelFields < Schema , Model > ,
136131> = GetModelField < Schema , Model , Field > [ 'omit' ] extends true ? true : false ;
137132
138- // type DelegateUnionResult<
139- // Schema extends SchemaDef,
140- // Model extends GetModels<Schema>,
141- // Options extends QueryOptions<Schema>,
142- // SubModel extends GetModels<Schema>,
143- // Omit = undefined,
144- // > = SubModel extends string // typescript union distribution
145- // ? DefaultModelResult<Schema, SubModel, Options, Omit> & { [K in GetModelDiscriminator<Schema, Model>]: SubModel } // fixate discriminated field
146- // : never;
133+ // Flat scalar-only result for a single model (no delegate expansion). Used as the leaf case
134+ // in DelegateUnionResult so that we never call DefaultModelResult from within itself.
135+ type FlatModelResult <
136+ Schema extends SchemaDef ,
137+ Model extends GetModels < Schema > ,
138+ Omit ,
139+ Options extends QueryOptions < Schema > ,
140+ > = {
141+ [ Key in NonRelationFields < Schema , Model > as ShouldOmitField < Schema , Model , Options , Key , Omit > extends true
142+ ? never
143+ : Key ] : MapModelFieldType < Schema , Model , Key > ;
144+ } ;
145+
146+ // Builds a discriminated union from a delegate model's direct sub-models. Recursion depth
147+ // is tracked via a tuple (each level appends a `0` element); the hard stop at length 10
148+ // ensures the type terminates even for the generic SchemaDef case.
149+ // Each union branch fixes the parent discriminator field to the sub-model name.
150+ // When a sub-model is itself a delegate, we recurse into its own sub-models so all
151+ // concrete leaf types appear in the union, each picking up the accumulated
152+ // discriminator overrides from both levels.
153+ type DelegateUnionResult <
154+ Schema extends SchemaDef ,
155+ Model extends GetModels < Schema > ,
156+ Options extends QueryOptions < Schema > ,
157+ SubModel extends GetModels < Schema > ,
158+ Omit = undefined ,
159+ Depth extends readonly 0 [ ] = [ ] ,
160+ > = Depth [ 'length' ] extends 10 // hard stop so generic SchemaDef never infinite-loops
161+ ? SubModel extends string
162+ ? FlatModelResult < Schema , SubModel , Omit , Options > &
163+ { [ K in GetModelDiscriminator < Schema , Model > ] : SubModel }
164+ : never
165+ : SubModel extends string // typescript union distribution
166+ ? IsDelegateModel < Schema , SubModel > extends true
167+ ? // sub-model is itself a delegate — recurse into its own sub-models so all
168+ // concrete leaf types appear in the union, each picking up the accumulated
169+ // discriminator overrides from both levels
170+ DelegateUnionResult <
171+ Schema ,
172+ SubModel ,
173+ Options ,
174+ GetSubModels < Schema , SubModel > ,
175+ Omit ,
176+ [ ...Depth , 0 ]
177+ > & { [ K in GetModelDiscriminator < Schema , Model > ] : SubModel }
178+ : // leaf model — produce a flat scalar result and fix the discriminator
179+ FlatModelResult < Schema , SubModel , Omit , Options > &
180+ { [ K in GetModelDiscriminator < Schema , Model > ] : SubModel }
181+ : never ;
147182
148183type ModelSelectResult <
149184 Schema extends SchemaDef ,
0 commit comments