fix(drizzle-zod): give createSchemaFactory's TCoerce generic a default, fixing refine callback any (#5968)#6031
Open
dumbCodesOnly wants to merge 1 commit into
Conversation
…fine callback to type as `any` Fixes drizzle-team#5968. createInsertSchema/createSelectSchema/createUpdateSchema at the top level are hardcoded consts typed <undefined> for their TCoerce parameter. But createSchemaFactory<TCoerce extends CoerceOptions>(options?) is generic with no default. When the factory is called without a `coerce` option (e.g. `createSchemaFactory({ zodInstance: z })`), TypeScript has no call-site candidate to infer TCoerce from, and falls back to the full constraint union (Partial<Record<'bigint'|'boolean'|'date'|'number'|'string', true>> | true | undefined) instead of `undefined`. That wider TCoerce flows into BuildRefine -> GetZodType -> CanCoerce, whose conditional types distribute over the union, and the refine callback's `schema` parameter collapses to `any` (TS7006 under noImplicitAny/strict). This reproduces with ANY createSchemaFactory call that omits `coerce`, regardless of which zod instance is passed (plain zod/v4 or an extended instance like @hono/zod-openapi's z) -- it isn't specific to extended instances as originally reported; the reporter's extended-instance case just happened to be the one that surfaced it. Fix: give TCoerce a default of `undefined`, matching the implicit default the hardcoded top-level consts already have. Verified against the built package (patched .d.ts/.d.cts via patch-package) with real tsc: - Before: `createSchemaFactory({ zodInstance: z })` (any zod instance) -> refine callback schema is `any`, TS7006 under --strict --noImplicitAny. - After: refine callback schema is a properly typed ZodType in all cases, including with an extended instance, with plain zod, and with an explicit `coerce` option (e.g. `coerce: { number: true }` still infers a coerced-number schema correctly, not `any` -- the default doesn't regress explicit coerce usage). Full repro + CI verification (all 4 cases, all clean, workflow green): https://github.com/dumbCodesOnly/drizzle-issue-5968-repro Run: https://github.com/dumbCodesOnly/drizzle-issue-5968-repro/actions/runs/29641936968
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.
Fixes #5968.
Root cause
createInsertSchema/createSelectSchema/createUpdateSchemaat the top level are hardcoded consts typed<undefined>for theirTCoerceparameter. ButcreateSchemaFactory<TCoerce extends CoerceOptions>(options?)is generic with no default.When the factory is called without a
coerceoption — e.g.TypeScript has no call-site candidate to infer
TCoercefrom (onlyzodInstanceis passed), so it falls back to the full constraint union (Partial<Record<'bigint'|'boolean'|'date'|'number'|'string', true>> | true | undefined) instead ofundefined.That wider
TCoerceflows intoBuildRefine→GetZodType→CanCoerce, whose conditional types distribute over the union, and the refine callback'sschemaparameter collapses toany(TS7006undernoImplicitAny/strict):Correction to the original report: this isn't specific to extended Zod instances (e.g.
@hono/zod-openapi'sz). It reproduces with anycreateSchemaFactorycall that omitscoerce, including plainzod/v4. The extended-instance case in the original issue just happened to be the one that surfaced it — I verified factory + plain zod hits the identicalTS7006on the real published package.Fix
Give
TCoercea default ofundefined, matching the implicit default the hardcoded top-level consts already have:Verification
Tested against the real built package (patched
.d.ts/.d.ctsviapatch-packagesince I verified the type-level cause there first) with realtsc --strict --noImplicitAny:createSchemaFactory({ zodInstance: z })— any zod instance, factory omittingcoerce— refine callbackschemaisany.schemais a properly typedZodTypein all cases: extended instance, plain zod, and also with an explicitcoerceoption (e.g.coerce: { number: true }still correctly infers a coerced-number schema — the default doesn't regress explicitcoerceusage).Full repro + CI verification (4 cases — original repro, plain-zod-factory control, no-factory control, explicit-coerce control — all clean, workflow green):
Happy to add a type-test case under
drizzle-zod/testsif maintainers want one in-repo rather than just linked externally — let me know the preferred location/pattern and I'll add it.