@@ -127,25 +127,35 @@ export type WhereCallback<TContext extends Context> = (
127
127
* SelectValue - Union of all valid values in a select clause
128
128
*
129
129
* This type defines what can be used as values in the object passed to `select()`.
130
- * It includes:
131
130
*
132
131
* **Core Expression Types**:
133
132
* - `BasicExpression`: Function calls like `upper(users.name)`
134
133
* - `Aggregate`: Aggregations like `count()`, `avg()`
135
134
* - `RefProxy/Ref`: Direct field references like `users.name`
136
135
*
136
+ * **JavaScript Literals** (for constant values in projections):
137
+ * - `string`: String literals like `'active'`, `'N/A'`
138
+ * - `number`: Numeric literals like `0`, `42`, `3.14`
139
+ * - `boolean`: Boolean literals `true`, `false`
140
+ * - `null`: Explicit null values
141
+ *
137
142
* **Advanced Features**:
138
143
* - `undefined`: Allows optional projection values
139
144
* - `{ [key: string]: SelectValue }`: Nested object projection
140
145
* - `PrecomputeRefStructure<any>`: Spread operations like `...users`
141
146
*
142
- * **Internal Support** (for spread operations):
143
- * - `true`, `Array<string>`, `any`: Allow RefProxy internal properties to pass through
147
+ * Note: The runtime spread implementation uses internal RefProxy properties
148
+ * but these are hidden from the type system for cleaner typing.
144
149
*
145
- * The nested object support enables projections like :
150
+ * Examples :
146
151
* ```typescript
147
152
* select({
148
153
* id: users.id,
154
+ * name: users.name,
155
+ * status: 'active', // string literal
156
+ * priority: 1, // number literal
157
+ * verified: true, // boolean literal
158
+ * notes: null, // explicit null
149
159
* profile: {
150
160
* name: users.name,
151
161
* email: users.email
@@ -159,13 +169,14 @@ type SelectValue =
159
169
| RefProxy
160
170
| RefProxyFor < any >
161
171
| Ref < any >
162
- | undefined
172
+ | string // String literals
173
+ | number // Numeric literals
174
+ | boolean // Boolean literals
175
+ | null // Explicit null
176
+ | undefined // Optional values
163
177
| { [ key : string ] : SelectValue }
164
178
| PrecomputeRefStructure < any >
165
179
| Array < Ref < any > >
166
- | true // For __refProxy property in spreads
167
- | Array < string > // For __path property in spreads
168
- | any // For __type property in spreads
169
180
170
181
/**
171
182
* SelectObject - Wrapper type for select clause objects
@@ -194,6 +205,12 @@ export type SelectObject<
194
205
* - `BasicExpression<T>` → `T`: Function results like `upper()` → `string`
195
206
* - `Aggregate<T>` → `T`: Aggregation results like `count()` → `number`
196
207
*
208
+ * **JavaScript Literals** (pass through as-is):
209
+ * - `string` → `string`: String literals remain strings
210
+ * - `number` → `number`: Numeric literals remain numbers
211
+ * - `boolean` → `boolean`: Boolean literals remain booleans
212
+ * - `null` → `null`: Explicit null remains null
213
+ *
197
214
* **Nested Objects** (recursive):
198
215
* - Plain objects are recursively processed to handle nested projections
199
216
* - RefProxy objects are detected and excluded from recursion
@@ -205,10 +222,10 @@ export type SelectObject<
205
222
* Example transformation:
206
223
* ```typescript
207
224
* // Input:
208
- * { id: Ref<number>, name: Ref<string>, profile: { bio: Ref<string> } }
225
+ * { id: Ref<number>, name: Ref<string>, status: 'active', count: 42, profile: { bio: Ref<string> } }
209
226
*
210
227
* // Output:
211
- * { id: number, name: string, profile: { bio: string } }
228
+ * { id: number, name: string, status: 'active', count: 42, profile: { bio: string } }
212
229
* ```
213
230
*/
214
231
export type ResultTypeFromSelect < TSelectObject > = {
@@ -230,6 +247,14 @@ export type ResultTypeFromSelect<TSelectObject> = {
230
247
? T
231
248
: TSelectObject [ K ] extends RefProxyFor < infer T >
232
249
? T
250
+ : TSelectObject [ K ] extends string
251
+ ? string
252
+ : TSelectObject [ K ] extends number
253
+ ? number
254
+ : TSelectObject [ K ] extends boolean
255
+ ? boolean
256
+ : TSelectObject [ K ] extends null
257
+ ? null
233
258
: TSelectObject [ K ] extends undefined
234
259
? undefined
235
260
: TSelectObject [ K ] extends { __type : infer U }
@@ -481,6 +506,7 @@ export type RefProxyFor<T> = IsExactlyUndefined<T> extends true
481
506
* 2. **Recursive structure**: Object properties become nested RefProxy/Ref types
482
507
* 3. **Optionality handling**: undefined/null are placed outside refs for ?. support
483
508
* 4. **Type preservation**: The structure mirrors the original type as closely as possible
509
+ * 5. **Spread support**: Internal properties are hidden during spread operations
484
510
*
485
511
* Examples:
486
512
* RefProxy<string> → { __refProxy: true, __path: [...], __type: string }
@@ -493,6 +519,7 @@ export type RefProxyFor<T> = IsExactlyUndefined<T> extends true
493
519
* - For primitive types: Only internal metadata is added
494
520
* - For object types: Properties are recursively transformed with optionality preserved
495
521
* - For undefined: No additional properties (just metadata)
522
+ * - For spreads: Internal properties are excluded from type checking
496
523
*/
497
524
export type RefProxy < T = any > = {
498
525
/** @internal - Runtime marker to identify ref proxy objects */
@@ -526,19 +553,19 @@ export type RefProxy<T = any> = {
526
553
/**
527
554
* Ref - The user-facing ref type with clean IDE display
528
555
*
529
- * This is what users see in their IDE and what they use in composable functions.
530
- * It's actually just an alias for RefProxy<T>, but TypeScript displays it cleanly as "Ref<T>"
531
- * instead of showing all the internal RefProxy properties .
556
+ * This is just an alias for RefProxy<T> but provides a cleaner name that
557
+ * TypeScript will prefer to display in most contexts. While not a perfect
558
+ * solution for hiding internal structure, it provides better semantics .
532
559
*
533
560
* Examples in IDE:
534
- * - Ref<string> instead of { __refProxy: true; __path: Array<string>; __type: string }
535
- * - Ref<User> instead of the full RefProxy<User> structure
536
- *
537
- * This unification means:
538
- * 1. Users get a clean API: all composables use Ref<T>
539
- * 2. Full functionality : Ref<T> has all RefProxy properties due to the alias
540
- * 3. Type compatibility: Ref<T> and RefProxy<T> are interchangeable
541
- * 4. Better DX: IDEs show meaningful type names instead of complex structures
561
+ * - Ref<string> (when TypeScript chooses to show the alias name)
562
+ * - RefProxy internals (when TypeScript expands the type)
563
+ *
564
+ * This alias approach means:
565
+ * 1. Users get a semantic API: all composables use Ref<T>
566
+ * 2. Full compatibility : Ref<T> is exactly RefProxy<T>
567
+ * 3. Type safety: No additional compatibility issues
568
+ * 4. Cleaner imports: Users import Ref instead of RefProxy
542
569
*/
543
570
export type Ref < T > = RefProxy < T >
544
571
0 commit comments