import type { Adaptations } from "../Adapter/Types"; import type { NaiveFabricator } from "../Fabricator/Types"; import type { Kind as SchemaKind } from "../Primitive"; import type { Adaptation, Kind, Meta, Produced, Produces } from "../Types"; import type { PlainObject } from "../Utility/Types"; /** * The broadest Schema constraint: some kind, some config. Used by compound * kinds (`object`/`array`) for their nested fields instead of the library-wide * Schema union `Primitive/index.ts` assembles — that union imports every kind's * `Core.ts`, so importing it back from within a kind's own `Core.ts` would * cycle. Mirrors how `array.Definition` uses the equally generic * `NaiveFabricator` for the same reason. */ export type AnySchema = { [Kind]: SchemaKind; [Meta]: PlainObject; [Adaptation]?: Adaptations; }; /** * Extract the value a Schema fabricates to, via the phantom, runtime-absent * `[Produces]` marker every kind's `Schema` carries. This is what lets * `object`/`array`'s per-field type extraction work uniformly across every kind * without importing and enumerating them (which would cycle, since * `object`/`array` can nest any kind, including each other) — the same role * `fabricate: () => $T` already plays for `NaiveFabricator`, one step * earlier, before a Schema has been built. `[Produces]` is always optional * (never assigned at runtime — see `DeepMerge`'s handling of optional-only keys * for why that matters), and what it holds is the value type boxed, * `Produced<…>` (`Types.ts`), never the value type itself. * * A built Fabricator carries no `[Produces]` — its value type lives on * `fabricate: () => $T` (`Fabricator/Types.ts`) — yet `toSchema` lets one stand * anywhere a Schema is expected: an `array` element, an `object` field, a * `tuple` slot, a `record` key or value, a wrapper's inner schema. So the * `NaiveFabricator` branch comes **first**, reading `$T` straight off * `fabricate`, and the `[Produces]` branch handles only what is left. That * order is load-bearing: a conditional matching an _optional_ property still * succeeds against a type that lacks it entirely (the same behavior * `AdaptationsOf` has to work around in `Adapter/Types.ts`), so a Fabricator * tested against `[Produces]` first matches, indexes a property it never had, * and collapses to `unknown` — `T.array(built)` silently becoming `unknown[]`. * `$Bindings` is not threaded into a Fabricator: it is already built, so any * `self` inside it was resolved when it was. `Fabrication.types.test.ts`'s * `NestedFabricatorAssertions` pin this. * * `$Bindings` is threaded down into every composite kind's children (each * `Core` is an `interface` carrying an optional `bindings`, and forwards * `NonNullable` into its own `[Produces]`). It is a * substitution slot for a self-reference, not a parameter of the schema the way * `$Definition`/`$Value` are — every composite forwards it unread, so whatever * `self` node is buried arbitrarily deep still receives it. A node whose * `[Produces]` reads `bindings[0]` (`recursive.self.Core`) can then close a * type-level fixed point, which is what makes a self-referential schema resolve * rather than collapse to `unknown`. Mirrors TypeBox's `Static = (T & { * params: P })['static']`, renamed because "params" reads as configuration and * this is closer to a de-Bruijn-style resolution environment — kept as a tuple, * as TypeBox's is, so a second bound self-reference could occupy index 1 later * without restructuring, though nothing today defines what that would mean. * `test/FabricatedBindings.types.test.ts` is the only thing asserting any of * it. * * Three details here are load-bearing, and each looks simplifiable: * * - **Indexed access, not a conditional `infer`.** Writing this as `($Schema & { * bindings: $Bindings }) extends { [Produces]?: Produced } ? ...` * fails with `TS2615` — the bindings do not collapse, and the error surfaces * as `unknown[] & [...]`. * - **`Exclude<…, undefined>` is applied to the box, never to a value type.** * Indexing an optional property adds `| undefined`, and `Exclude` strips * every `undefined` in the union it is handed. That is exact only because the * union is `Produced<…> | undefined`: the box is never `undefined`, so the * one stripped is the modifier's, and the _legitimate_ `undefined` in * `nullish`/`undefinable`/`object.optional`/`undefined`'s own value types * sits inside the box, out of reach. No reader of an _unboxed_ value type can * be exact: `Required<>` spares a declared `undefined` only when the consumer * compiles with `exactOptionalPropertyTypes`, and strips it like `Exclude` * when they do not. `Required<…>[typeof Produces]["value"]` is not a way to * keep `Required<>` either — it fails `TS2536`. * `test/Fabricated.types.test.ts`, compiled with the flag off by * `tsconfig.inexact.json`, is the guard. * - **The `extends` guard names the box, and belongs inside, not on the * parameter.** Matched against `{ [Produces]?: unknown }`, the read has no * `"value"` to index (`TS2536`). Constraining `$Schema` instead would force * `[Produces]` onto `AnySchema` (which deliberately omits it), and that * shifts variance enough to break `.as`'s contravariant parameter check in * `Fabrication.types.test.ts`. */ export type Fabricated<$Schema, $Bindings extends unknown[] = []> = $Schema extends NaiveFabricator ? $T : $Schema extends { readonly [Produces]?: Produced; } ? Exclude<($Schema & { bindings: $Bindings; })[typeof Produces], undefined>["value"] : unknown;