annotate a republished binding as typeof the binding it republishes, which ZodType widened into a lie - #238
Merged
Conversation
…which ZodType widened into a lie
An item that publishes another item's binding — an alias's const, a one-slot
tuple struct over a brand, and the generic `$SchemaDefault` of both — annotated
it `ZodType<T>`. Over a branded schema that annotation does not hold:
`$ZodBranded`'s output is the branded type while `ZodType<T>`'s parameter is
the unbranded one, and tsc --strict refuses the assignment. Thirteen such
errors on one emitted module, across every brand inner and in both declaration
orders.
The distinction the fix draws is structural, not nominal: an item that builds
its own expression keeps `ZodType<Name>`, and an item that restates a sibling's
binding says so — `typeof {Name}$RawSchema`, the one annotation that is true
whatever the sibling's class is. The generic default binds its factory call to
a raw const first so the same `typeof` applies. That is why the registry answer
was rejected on measurement: an alias written above its brand has no entry to
read, and an alias chain re-breaks at the second link — one macro invocation
sees one item, and `typeof` needs to know nothing about the sibling but its
name. The `ReturnType<typeof Factory>` spelling type-checks while silently
collapsing an inferred value to the opaque parameter, which is the trap
assignability alone does not catch.
Nothing that builds its own expression moves: an alias of a `String`, of a
`Vec`, of a map, every composite over a brand, and every brand's own binding
are pinned byte-identical, and no pre-existing expectation string changed.
Measured on the crate's real emission: thirteen TS2322s before, zero after,
declaration emit clean, and the inferred type of every parse stays exact.
just fmt, just check, just quick, just lint-all across all 68 toggles, and the
test powerset across all 68 in four partitions — all green.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
An item that publishes another item's binding — an alias's const, a one-slot
tuple struct over a brand, and the generic
$SchemaDefaultof both — annotatedit
ZodType<T>. Over a branded schema that annotation does not hold:$ZodBranded's output is the branded type whileZodType<T>'s parameter isthe unbranded one, and tsc --strict refuses the assignment. Thirteen such
errors on one emitted module, across every brand inner and in both declaration
orders.
The distinction the fix draws is structural, not nominal: an item that builds
its own expression keeps
ZodType<Name>, and an item that restates a sibling'sbinding says so —
typeof {Name}$RawSchema, the one annotation that is truewhatever the sibling's class is. The generic default binds its factory call to
a raw const first so the same
typeofapplies. That is why the registry answerwas rejected on measurement: an alias written above its brand has no entry to
read, and an alias chain re-breaks at the second link — one macro invocation
sees one item, and
typeofneeds to know nothing about the sibling but itsname. The
ReturnType<typeof Factory>spelling type-checks while silentlycollapsing an inferred value to the opaque parameter, which is the trap
assignability alone does not catch.
Nothing that builds its own expression moves: an alias of a
String, of aVec, of a map, every composite over a brand, and every brand's own bindingare pinned byte-identical, and no pre-existing expectation string changed.
Measured on the crate's real emission: thirteen TS2322s before, zero after,
declaration emit clean, and the inferred type of every parse stays exact.
just fmt, just check, just quick, just lint-all across all 68 toggles, and the
test powerset across all 68 in four partitions — all green.