@@ -229,14 +229,16 @@ export const outputContract = (
229229} ;
230230
231231/**
232- * One input field, in four flavours:
232+ * One input field, in five flavours:
233233 * - a `v.var()`, whose shape comes from the var's own `schema`
234234 * - a handler-less `v.fn(...)` builder, which types the field as a FN
235235 * - a type from `v.string()` / `v.object()` / ...
236236 * - a bare nested record, which recurses
237+ * - anything else (e.g. `null`, `"lit"`) kept as itself - so
238+ * `v.union([user, null])` infers `User | null`, not just `User`
237239 *
238- * The record case has to come last : a TypeDefination is itself a record,
239- * and so is a builder.
240+ * The record case has to come after TypeDefination : a TypeDefination is
241+ * itself a record, and so is a builder.
240242 */
241243type FieldOut < F > = F extends { $var : true ; schema ?: infer S }
242244 ? InferInput < NonNullable < S > >
@@ -246,7 +248,7 @@ type FieldOut<F> = F extends { $var: true; schema?: infer S }
246248 ? OutputOf < F >
247249 : F extends Record < string , unknown >
248250 ? Prettify < { [ K in keyof F ] : FieldOut < F [ K ] > } >
249- : never ;
251+ : F ;
250252
251253type FieldIn < F > = F extends { $var : true ; schema ?: infer S }
252254 ? InferArgs < NonNullable < S > >
@@ -256,7 +258,7 @@ type FieldIn<F> = F extends { $var: true; schema?: infer S }
256258 ? T
257259 : F extends Record < string , unknown >
258260 ? ArgsShape < F >
259- : never ;
261+ : F ;
260262
261263/**
262264 * `optional: true` on `v.fn.type` widens the same way a type's `optional`
@@ -359,7 +361,21 @@ export const asType = (value: any): TypeDefination<any, any> =>
359361 } as TypeDefination < any , any > )
360362 : isType ( value )
361363 ? value
362- : { name : "object" , shape : value } ;
364+ : // Bare literals in `v.union([schema, null])` / `["a", "b"]`:
365+ // map to a type whose `name` matches `typeOf`, with `enum`
366+ // pinning the exact value for string/number/boolean.
367+ value === null
368+ ? ( { name : "null" } as TypeDefination < null , null > )
369+ : value === undefined
370+ ? ( { name : "undefined" } as TypeDefination < undefined , undefined > )
371+ : typeof value === "string" ||
372+ typeof value === "number" ||
373+ typeof value === "boolean"
374+ ? ( {
375+ name : typeof value ,
376+ enum : [ value ] ,
377+ } as TypeDefination < any , any > )
378+ : { name : "object" , shape : value } ;
363379
364380/**
365381 * Replace the whole `$attrs` bag. On a var, rebinds `customize` so a later
0 commit comments