Skip to content

fix: make generated types agree with their valibot schemas - #28

Merged
maxholman[bot] merged 5 commits into
masterfrom
fix/schema-types
Sep 27, 2026
Merged

maxholman[bot] merged 5 commits into
masterfrom
fix/schema-types

Conversation

@maxholman

@maxholman maxholman Bot commented Sep 27, 2026

Copy link
Copy Markdown
Contributor

Each commit fixes one way the generated TS type and its valibot schema disagreed. Each comes with whole-file snapshot specs, and the fixtures are regenerated. It works on the published rest-client 15.

  • Records: additionalProperties records are keyed by string, not string | number.
  • Nullable: nullable objects, arrays and strings admit null. A combinator folds in its members' nullable: a union is nullable if any member is, an intersection only if every member is.
  • required: an object schema that omits type still honours required. openai's OpenAIFile.status_details is now optional.
  • Component order: component schemas are ordered by every nested $ref (items objects, additionalProperties, combinators beside properties). Before, a schema could register ahead of its target, which gave never in TS and v.unknown() in valibot, and silently dropped union members (openai RunObject.tools). An unresolved ref now throws.
  • Open values: a value a schema leaves open ({}, an untyped record value, an array without items) is JsonValue in TS. In valibot it's a recursive jsonValueSchema, emitted only in a module that uses it. Before, TS said Jsonifiable, valibot said unknown, and an empty schema only accepted objects.

Stacked on #27.

maxholman Bot and others added 5 commits September 27, 2026 14:44
JSON object keys are always strings, so Record<string | number, T> made
Object.entries-style helpers type keys as string | number.

Co-Authored-By: LLM <noreply@block65.dev>
valibot wraps every nullable schema in v.nullable, but the TS type for an
object, an array, or a non-temporal string returned before the null union
was applied. A validated response could then hold null where its type
said a value was always there, as docker's IPAMConfig does.

Co-Authored-By: LLM <noreply@block65.dev>
A property was optional only when its parent declared `type: "object"`,
so a schema with properties and no type made every property required,
while valibot made the unlisted ones optional. The parent is now judged
by isObjectSchema, as the type dispatch already is.

Co-Authored-By: LLM <noreply@block65.dev>
getDependents followed a $ref only at a few top-level positions, so a ref
under an items object, an additionalProperties value, or combinators beside
properties was left out of the sort. Its schema could register before its
target, and the TS side then emitted `never` while valibot emitted
v.unknown(), which dropped out of unions (openai's RunObject.tools). It
now walks the whole schema, and an unresolved ref throws on both sides
rather than emitting a placeholder.

Co-Authored-By: LLM <noreply@block65.dev>
An empty schema, an untyped record value, or an array without items was
Jsonifiable in TS, which admits toJSON objects that JSON.parse never
yields. valibot typed the same values as unknown, and an empty schema
there required a record, rejecting other JSON values. Both sides now use
type-fest's JsonValue: valibot checks the value recursively with a lazy
union typed v.GenericSchema<JsonValue>, emitted only in a module that
uses it.

Co-Authored-By: LLM <noreply@block65.dev>
@maxholman
maxholman Bot changed the base branch from build/tooling to master September 27, 2026 07:28
@maxholman maxholman Bot closed this Sep 27, 2026
@maxholman maxholman Bot reopened this Sep 27, 2026
@maxholman
maxholman Bot merged commit 97e4a3d into master Sep 27, 2026
1 check passed
@maxholman
maxholman Bot deleted the fix/schema-types branch September 27, 2026 13:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants