Skip to content

fix: infer each generated schema's own concrete type - #6

Merged
Mearman merged 1 commit into
mainfrom
fix/infer-concrete-schema-types
Sep 10, 2026
Merged

fix: infer each generated schema's own concrete type#6
Mearman merged 1 commit into
mainfrom
fix/infer-concrete-schema-types

Conversation

@Mearman

@Mearman Mearman commented Sep 10, 2026

Copy link
Copy Markdown
Member

Discovered while wiring wire-mesh's ts/packages/core to consume the generated schemas: every single generated type (Frame, HandshakeFrame, TokenClaims, all 70 of them against the real spec) was unknown, not the schema's actual shape.

Root cause: every schema constant was declared `export const xSchema: z.ZodType = z.lazy(() => ...)`, and the explicit `: z.ZodType` annotation forces the generic base type onto the const regardless of what the schema actually validates. `z.infer` then resolves to `unknown` for every rule, defeating the entire point of generating a schema with an attached inferred type.

The annotation exists to satisfy TypeScript's circular-inference restriction, which only actually applies to a rule genuinely part of a `z.lazy()` reference cycle (e.g. `frame`'s own union includes `federation-envelope-frame`, whose `inner` field is `.cbor frame` -- a reference straight back to `frame`). Every emit* function now collects which other rule names a rule's own expression references via `z.lazy()`; `emitModule` builds the full reference graph afterwards and only keeps the explicit annotation on rules that are actually cyclic.

Verified against wire-mesh's real spec: 0 of 70 rules turn out to be cyclic, so the generated module compiles with zero explicit annotations now, and `Frame`/`HandshakeFrame` are confirmed genuinely concrete (accessing a real field typechecks, accessing a made-up one via `@ts-expect-error` is correctly flagged -- both would silently pass under `any`/`unknown`).

Every schema was declared as "export const xSchema: z.ZodType = ...",
forcing the generic base type onto every const regardless of its own
shape. z.infer<typeof xSchema> then collapsed to unknown for every
single rule, not just the genuinely recursive ones -- the entire point
of generating a schema with an attached inferred type was defeated.

Only a rule that's actually part of a z.lazy() reference cycle (e.g.
frame's own union includes federation-envelope-frame, whose inner
field is .cbor frame -- a reference straight back to frame) needs the
explicit annotation to satisfy TypeScript's circular-inference check;
every other rule can infer its own concrete shape. mergeRules/emit*
now collect which rule names each rule's own expression references via
z.lazy(), and emitModule computes the actual reference graph across
all rules to decide, per rule, whether it needs the annotation.

Verified against wire-mesh's real spec: 0 of 70 rules are in a
reference cycle, so the generated module now compiles with zero
explicit annotations, and each generated type (e.g. Frame,
HandshakeFrame) is confirmed concrete -- both accessing a real field
and a deliberately nonexistent one via @ts-expect-error typecheck as
expected, which would both silently pass under `any`/`unknown`.
@Mearman
Mearman marked this pull request as ready for review September 10, 2026 09:20
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 10, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
🔒 Security Review Completed 2026-09-10T09:26:06.434704Z 886f7c1 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@Mearman
Mearman merged commit 5ea7ef6 into main Sep 10, 2026
1 check passed
@Mearman
Mearman deleted the fix/infer-concrete-schema-types branch September 10, 2026 09:26
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.

1 participant