Skip to content

finding(types): the zod BaseSchema mirror still declares visible/disabled as boolean and ariaLabel as string, after the TS declarations widened #4605

Description

@yinlianghui

Filed by the #4581 seat (PR #4603) while measuring that PR's published .d.ts. Not fixed there — widening a published runtime validator is a contract decision of its own, and #4581's card scoped to the TS declarations.

The drift

packages/types/src/zod/base.zod.ts is a runtime validator mirroring BaseSchema. Three of its keys are now NARROWER than the TypeScript declaration they mirror:

Key BaseSchema (TS, src/base.ts) BaseSchemaCore (zod, src/zod/base.zod.ts) Widened by
visible boolean | string (:119) z.boolean().optional() (:100) PR #4593 (#4581)
disabled boolean | string z.boolean().optional() (:127) PR #4603 (#4581)
ariaLabel string | KeyedI18nLabel z.string().optional() (:142) PR #4603 (#4581)

So an author can now write disabled: "${data.status === 'locked'}" — a capability the renderer implements (SchemaRenderer.tsx:466 evaluates it through evaluateCondition) and the type declares — and BaseSchemaCore.parse() will reject it. .passthrough() does not rescue any of the three: it admits UNDECLARED keys, and all three are explicitly declared, so the declared narrow validator wins.

Why this is observation-class rather than a live defect

Measured, nothing enforces it on the render path today:

  • packages/core/src/validation/schema-validator.ts — the validateSchema that SchemaRenderer calls — imports only the TS type BaseSchema (:19). It is hand-written and never touches the zod schema.
  • SchemaRenderer.tsx:502 computes _validation under __DEV__ only, and the sole consequence is a data-obj-schema-invalid attribute (:631). No hard reject.
  • The zod BaseSchema value is consumed only by its siblings inside packages/types/src/zod/** (app, blocks, theme, form, navigation, reports, feedback, views, … all .extend(...) it).

So no user hits this today. It matters because @object-ui/types/zod is a PUBLISHED validator surface: any consumer that parses an authored schema with it gets a rejection for a spelling the published types invite. That is the "declared = enforced" property inverted — the type says yes, the validator says no.

Also worth a decision, not a guess

Widening the zod side is not mechanical, which is exactly why this is a card and not a drive-by:

There is a second, larger question underneath: whether this hand-written zod mirror should track BaseSchema by construction (derivation or a parity test) rather than by hand, so the next widening cannot silently drift again. #2231 asks the adjacent question for ListViewSchema against @objectstack/spec/ui; this one is BaseSchema against its OWN TypeScript declaration, so it is filed standalone rather than as a sub-issue.

Refs #4581, PR #4593, PR #4603, #4580 (comment 5284007579), #4167.


Generated by Claude Code

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions